支持镜像签名验证¶
https://blueprints.launchpad.net/cinder/+spec/cinder-support-image-signing
Cinder 目前不支持已下载签名镜像的签名验证。为 Cinder 赋予验证镜像签名的能力将为最终用户提供更强的镜像数据完整性保证,他们使用这些镜像数据来创建卷。此更改将使用与 Glance 中配套功能相同的数据模型来存储镜像元数据,这将允许最终用户对镜像进行签名并在上传时验证这些镜像签名 [1]。
问题描述¶
此前,OpenStack 防御意外镜像修改的方法仅限于验证 MD5 校验和。虽然这对于防止意外修改可能足够,但 MD5 是一种哈希函数,而不是身份验证原语 [2],因此无法防止故意、恶意的镜像修改。镜像在传输过程中可能会被修改,例如在上传到 Glance 或传输到 Cinder 时。被修改的镜像可能包含恶意代码。
目前,Glance 支持在上传时进行镜像签名验证,但 Cinder 不支持此功能,以确保在使用镜像之前镜像数据的完整性。提供签名验证支持将允许 Cinder 在从镜像创建卷之前验证签名。此功能将保护 OpenStack 免受以下攻击场景的影响
中间人攻击 - 具有 Cinder 和 Glance 之间网络访问权限的攻击者正在更改 Cinder 从 Glance 下载数据时的数据。攻击者可能会将恶意软件合并到镜像中和/或更改镜像元数据。
不受信任的 Glance - 在混合云部署中,Glance 托管在物理位置不安全的机器上,或由安全基础设施有限的公司托管。攻击者可能能够通过物理访问主机机器或通过托管 Glance 公司的网络安全措施不足,来破坏 Glance 和/或 Glance 存储的镜像的完整性。
请注意,我们的威胁模型仅考虑镜像在最终用户和 Glance 之间传输、在 Glance 中静态存储以及在 Glance 和 Cinder 之间传输时的完整性威胁。此威胁模型不包括,因此此功能也不会解决,对 Cinder 的完整性、可用性或机密性的威胁。
用例¶
用户希望高度确信他们上传到 Glance 的自定义镜像在创建卷之前没有被意外或恶意修改。
通过此提议的更改,Cinder 将在下载镜像时验证已签名镜像的签名。如果无法验证镜像签名,则 Cinder 将不会从该镜像创建卷,而是将卷置于错误状态。用户将通过 Glance API 的 image-create 方法将镜像和镜像签名元数据上传到 Glance来开始使用此功能。所需的镜像签名元数据属性如下
img_signature - 镜像数据签名的 base 64 编码的字符串表示形式。
img_signature_hash_method - 指定用于签名的哈希方法的字符串。当前,支持的值为 SHA-224、SHA-256、SHA-384 和 SHA-512。MD5 和其他加密安全性较弱的哈希方法将不在此字段中支持。使用不受支持的哈希算法签名的任何镜像将无法通过验证。
img_signature_key_type - 指定用于生成签名的签名方案的字符串。有关当前支持的详细信息,请查看 Glance 的文档 [7]。
img_signature_certificate_uuid - 编码用于从密钥管理器检索证书的证书 uuid 的字符串。
Glance 中的镜像验证功能使用 cursive.signature_utils 模块来验证签名元数据,然后再存储镜像。如果签名无效或元数据不完整,则此 API 方法将返回 400 错误状态并将镜像置于“killed”状态。请注意,如果签名元数据只是不存在,则镜像将像往常一样存储。
然后,用户将使用 Cinder API 的 volume create 方法从该镜像创建卷。如果 cinder.conf 中的 verify_glance_signatures 标志设置为“True”,Cinder 将调用 Glance 获取镜像的属性,其中包括用于镜像签名验证的属性。Cinder 将将镜像数据和镜像属性传递给签名验证模块,该模块将验证签名。如果签名验证失败,或者镜像签名元数据不完整或缺失,则从镜像创建卷将失败,并且 Cinder 将记录异常。如果签名验证成功,Cinder 将从镜像创建卷并记录一条消息,指示镜像签名验证成功以及有关签名证书的详细信息。
提议的变更¶
由于 Nova 已经实现了此功能,并且所有验证过程都已移动到 cursive 模块 [4],因此现在在 Cinder 中支持此功能更加方便。
使用证书验证镜像签名
我们建议通过将 cursive 纳入 Cinder 的控制流程,用于从镜像创建卷,并使用新的配置 verify_glance_signatures 来启用或禁用此功能来实现初始实现。最初它将有两个选项(默认值为 enabled)
enabled:当镜像具有完整的签名元数据时进行验证。disabled:关闭验证。
注意:我们讨论过添加 required 选项以引入对验证的严格模式,但由于我们无法在后端克隆镜像卷时进行验证,因此无法保证这一点。在我们可以覆盖每种方法时,仍将考虑严格模式。
在下载镜像时,Cinder 将同时检查新的配置标志和镜像的元数据。如果需要,该模块将使用 Glance 传递给 Cinder 的镜像属性执行镜像签名验证。如果验证失败,或者镜像签名元数据不完整或缺失,Cinder 将不会从该镜像创建卷。相反,Cinder 将抛出异常并记录错误。如果签名验证成功,Cinder 将继续创建卷。代码示例如下
if CONF.glance.verify_glance_signatures != 'disabled':
verifier = None
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:
#Image signature verification failed
# Collect image data
try:
if verifier:
verifier.verify()
except cryptography.exceptions.InvalidSignature:
#Image signature verification failed
注意:我们将尝试不同的方法来创建镜像卷,因此我们必须提到此功能不会涵盖每种方法,尤其是在后端创建卷时。
为了明确起见,我们仅在从 glance 下载镜像并且内容复制到主机上的卷时才验证镜像的签名。因此,无论配置选项和提供的签名元数据如何,在通过 clone_image 或 clone_image_volume 创建镜像卷时,我们将跳过此验证过程,为了不让最终用户感到困惑,我们将在卷的镜像元数据中添加验证标志 signature_verified,在从镜像创建时。
使用受信任证书验证证书
此功能试图找到一种确定用于生成和验证签名证书是否是用户信任的证书的方法,我们可以在 Nova 规范中找到更多详细信息 [5]。简而言之,在该功能中,最终用户还可以使用给定的受信任证书(通过 API 或配置选项指定)来验证镜像的证书。考虑到此功能目前正在 Nova 中添加,我们将会在其合并到 Nova 后,为了保持一致性,再跟进另一个规范。
备选方案¶
一种替代方案是直接对镜像数据进行签名,而是支持通过对镜像数据的哈希进行签名来创建签名。这会引入不必要的复杂性,需要额外的哈希阶段和额外的元数据选项。由于 Glance 社区对哈希镜像数据存在性能问题,开发人员最初实施了一种通过对 Glance 已经计算的 MD5 校验和进行签名的方法。由于 MD5 的安全弱点以及两次执行哈希运算和维护有关两种哈希算法的信息,Nova 社区拒绝了这种方法。
使用证书进行签名和签名验证的替代方案是使用公钥。但是,这种方法存在一个重大弱点,即攻击者可以在密钥管理器中生成自己的公钥,使用该公钥对篡改后的镜像进行签名,并将对他们公钥的引用与他们的签名镜像一起传递给 Cinder。或者,使用证书提供了一种将此类攻击归因于证书所有者的手段,并遵循常见的加密标准,将信任根置于证书颁发机构。
使用 verify_glance_signatures 配置标志指定 Cinder 应该执行镜像签名验证的替代方案是为卷类型使用“verify_glance_signatures”类型键,以指定应该从签名镜像创建的单个卷。用户在使用 Cinder CLI 从镜像创建卷时,将指定包含类型键“verify_glance_signatures=True”的卷类型,以指示镜像签名验证应作为创建卷的控制流程的一部分发生。这可能会在后续更改中添加,但不会包含在初始实现中。如果添加,则“verify_glance_signatures”类型键选项将与配置选项方法一起工作。在这种情况下,如果设置了配置标志,或者用户指定创建包含“verify_glance_signatures=True”类型键的卷类型的卷,Cinder 将执行镜像签名验证。
使用 verify_glance_signatures 配置标志指定 Cinder 应该执行镜像签名验证的另一种替代方案是修改 Cinder 创建命令以接受一个额外的参数,指定是否应发生镜像签名验证。这可能会在后续更改中添加,但不会包含在初始实现中。如果添加,则额外的参数将与配置选项方法一起工作。在这种情况下,如果设置了配置标志,或者用户指定使用额外的参数创建卷,Cinder 将执行镜像签名验证。
我们可能只需要选择上述两种替代方案中的一个。
数据模型影响¶
Glance 中配套的工作引入了用于镜像签名所需的其他 Glance 镜像属性。Cinder 中的初始实现将引入一个配置标志,指示 Cinder 在启动镜像之前是否应该执行镜像签名验证。
REST API 影响¶
无
安全影响¶
Cinder 目前缺乏在从它们创建卷之前验证镜像的机制。包含在镜像中的校验和可防止意外修改,但无法防止具有访问 Glance 或 Cinder 和 Glance 之间通信网络的攻击者。此功能促进了 Cinder 和 Glance 之间的逻辑信任边界的创建;此信任边界允许最终用户高度确信 Cinder 正在从受信任用户签名的镜像创建卷。
虽然 Cinder 将使用证书来执行此任务,但这些证书将由密钥管理器存储并通过 Castellan 访问。
通知影响¶
此更改将涉及添加日志消息以指示签名验证和创建的成功或失败。
后续更改将涉及在签名验证失败时通知用户,这将使用异步错误通知功能 [3]。
其他最终用户影响¶
如果签名验证失败,则 Cinder 将不会从镜像创建卷,并且将记录并记录错误消息。用户可以通过日志文件或 CLI 命令获取错误消息,并了解错误原因。在这种情况下,用户必须通过 Glance API 或 Horizon 界面编辑镜像的元数据;或者重新启动将正确的签名元数据上传到 Glance 的镜像,以便从镜像创建卷。
性能影响¶
仅当设置了 verify_glance_signatures 配置标志时,才会使用此功能。
当发生签名验证时,由于通过 Castellan 接口从密钥管理器检索证书,因此会产生延迟。与使用公钥解密签名相关的哈希镜像数据和 CPU 开销也会产生开销。
其他部署者影响¶
我们建议您部署 Barbican 服务 [6] 来存储您的证书信息,正如其他项目建议的那样,尽管您可以通过 Castellan 集成任何其他密钥管理服务 [8]。
开发人员影响¶
无
实现¶
负责人¶
- 主要负责人
ji-xuepeng TommyLike(tommylikehu@gmail.com)
- 其他贡献者
无
工作项¶
该功能将分以下阶段实现
向 Cinder 添加功能,该功能在 Cinder 下载 Glance 镜像并且设置了 verify_glance_signatures 配置标志时调用
cursive模块。在签名验证失败时生成通知消息。
依赖项¶
无
测试¶
将单元测试以及 barbican-tempest-plugin 中的 tempest 测试添加到覆盖从签名镜像创建卷的情况。
文档影响¶
需要记录有关如何使用此功能的说明。
参考资料¶
Cryptography API:https://pypi.ac.cn/project/cryptography/0.2.2