随着针对软件供应链攻击事件的频繁发生,保护我们的软件供应链至关重要。此外,在过去几年中,容器采用率也有所增加。根据这些信息,人们对容器镜像签名的需求日益增长,这可以帮助我们有效防止供应链攻击事件的发生。
此外,如今我们使用的大多数容器,即使我们在生产环境中使用它们也依然很容易遭受供应链攻击。在传统的 CI/CD 工作流中,我们生成镜像并将它们推送到注册表中。确保供应链安全的一个重要部分就是我们所构建镜像的完整性,这意味着我们必须确保我们构建的镜像未被篡改,同时这也意味着要保证我们从注册表中提取的镜像与我们将要部署到生产系统中的镜像相同。
证明镜像未被篡改的最简单有效的方法之一(感谢 Sigstore)是在构建镜像后立即对其进行签名,并在把它们部署到生产系统之前对其进行验证 。这就是Cosign和Kyverno发挥作用的地方。
Kyverno 是一个专为 Kubernetes 设计的开源策略引擎,对 Kubernetes 资源进行管理,而且编写策略不需要任何新语言。什么是策略引擎? 它是一款允许用户定义一组可用于验证、变异和生成 Kubernetes 资源的软件。作为一个CNCF沙箱项目,Kyverno开始获得社区的支持和关注。由于近年来软件供应链攻击事件的频繁发生,Kyverno越来越受欢迎。Kyverno 已通过支持验证镜像签名和 in-toto 认证进行工作负载保护。这些工作负载保护是通过cosign和SLSA框架实现的。
使用Cosign签名和验证
Cosign是一个用于容器镜像签名和验证的工具,由Linux基金会和Project Sigstore共同维护。除其他功能外,Cosign还支持KMS签名,内置二进制透明度以及Rekor和Kubernetes策略实施的时间戳服务。此外,Kyverno 也利用 Cosign 来验证容器镜像签名、认证等。
软件构件通常是不透明的blob,无法轻松检查其安全性,因此更常见的是推理它们是如何形成的,而不是它们中的内容。我们不能将策略应用于单独的代码行,而是应用策略来描述谁构建了软件,如何构建它,以及代码来自哪里。这个面包屑组件通常被称为软件的来源。
<!--如果您想获得有关出处和认证的更多详细信息,请参阅来自 丹·洛伦克(Dan Lorenc)的博客(@lore nc_dan)-->
用户不是直接对制品进行签名,而是创建一个文档,用于捕获他们签署制品背后的意图以及所做的任何特定声明,这些都将作为签名的一部分。虽然术语多种多样,但由In-Toto定义的分层模型似乎有很大的发展前景。
in-toto认证模式为元数据(如存储库和生成环境详细信息、漏洞扫描报告、测试结果、代码审查报告以及用于验证镜像完整性的其他信息)提供了灵活的方案。每个认证都包含一个带有predicateType 和predicate的签名语句。
从整体上考虑安全性并确保你正朝着更安全的方向努力可能是一件具有挑战性的事,而SLSA项目可以对此有所帮助。SLSA代表"软件构件的供应链级别",发音为"salsa"。作为一个安全框架,你可以将其视为一个规格和控件清单,用于防止篡改、提高完整性并确保你的项目、业务、和企业中软件包和基础架构的安全性。这就是你如何以足够安全的方式,在链条中的任何环节尽可能地保持弹性。将SLSA,Sigstore和Kyverno结合在一起可以为你奠定安全软件开发生命周期的坚实基础。
现在我们已经介绍了Kyverno提供的供应链安全功能的基本部分,让我们深入了解它如何在真实环境中实现所有这些功能。
Kyverno和Cosign中的Workload Identity
在下一节中,我们将使用Google Kubernetes Engine(GKE)和Google Cloud Key Management Service(KMS)等服务在Google Cloud Platform(GCP)上进行演示。正如Cosign部分所述,云提供商的KMS系统是cosign中的一流用户,这体现了Cosign与GCP KMS的完美配合。
GCP KMS是一种云服务,用于管理其他Google云服务的加密密钥,以便企业可以实现加密功能。云密钥管理服务允许你在单个集中式云服务中创建、导入和管理加密密钥并执行加密操作。
首先,我们需要在 GKE 上创建一个启用了Workedload Identity功能的 Kubernetes 集群。但在执行此操作之前,我们还应该详细了解Workload Identity,以及 Cosign 如何利用此功能对 GCP 服务(如 GCP KMS)进行授权调用。
GCP 提供了 Workload Identity 功能,它允许在 GKE 上运行的应用程序访问 Google Cloud API,例如 Compute Engine API、BigQuery Storage API 或 Machine Learning APIs。
Workload Identity允许 GKE 集群中的 Kubernetes 服务账户充当 IAM 服务账户。访问 Google Cloud APIs 时,使用已配置的 Kubernetes 服务帐户的 Pods会自动作为 IAM 服务帐户进行身份验证。你可以使用Workload Identity为集群中的每个应用程序分配不同的细粒度标识和授权。
此外,Workload Identity 是我们较为推荐的方式,在 Google Kubernetes Engine (GKE) 上运行的工作负载以安全且可管理的方式访问 Google Cloud 服务。幸运的是,我们不需要执行任何额外的操作即可在 GKE 上启用Workload Identity,因为 Cosign 可以通过提供使用Wordload Identity的环境凭据检测功能来支持使用Wordload Identity。 再次感谢 Dan Lorenc,他写了另一篇出色的博客来解释Workload Identity和 Ambient Credentials术语之间的关系。
在我们的示例中,Kyverno将在GKE上运行,因此我们将应用策略来验证容器镜像。 Kyverno 中的这种类型的规则是 verifyImages,如果在 OCI 注册表中找不到签名,或者未使用指定的密钥对镜像进行签名,则该规则将失效。它还会改变匹配的镜像,以便在尚未指定摘要时添加镜像摘要,使用镜像摘要会使镜像引用不可变。
在上面的策略示例中,Kyverno 在内部使用 Cosign SDK ,根据指定的密钥验证给定的镜像。假设我们使用 GCP KMS,Kyverno 必须向该服务进行身份验证才能正确进行 API 调用,在这里我们使用Workload Identity来实现这一点。
<!--你的代码是从环境中接受它所需要的凭据,而不是在代码旁部署机密。当然,这些必须来自某个地方 - 但如今平台提供商承担了存储,分发,刷新和撤销机密的责任。应用程序可以直接从环境中按需读取环境凭据,而不是将长期机密作为生成/部署过程的一部分进行预配,该过程需要持续很长时间才能运行二进制文件。-->
在撰写本文时,Cosign支持为四个不同的系统提供环境凭据检测,包括GitHub,SPIFFE,文件系统和Google。你可以在以下问题中看到我们为 GCP KMS 添加的支持:
有关更多详细信息,请查看 GitHub Cosign 存储库中的提供程序目录。
演示
本节将对在 GKE 上运行 Kyverno 策略来验证容器镜像进行演示,如果在 OCI 注册表中找不到签名,或者未使用指定的密钥对其进行签名,则此规则 (verifyImages) 将失效。它还会改变匹配的镜像,以便在尚未指定摘要时添加镜像摘要。通过使用镜像摘要,我们的镜像引用是不可变的。
先决条件
- kubectl v1.20+
- gcloud v375.0.0
- cosign v1.6.0
首先,我们需要在 GKE 上创建一个启用了Workload Identity功能的 Kubernetes 集群。我们将以 PROJECT_ID.svc.id.goog 的形式使用固定的Workload Identity库。
当你在集群上启用Workload Identity时,GKE 会自动为集群的 Google Cloud 项目 创建一个固定的Workload Identity库,它可以让 IAM 了解和相信 Kubernetes 服务账户凭证。GKE 将此库用于项目中使用 Workload Identity 的所有集群。

https://gist.github.com/developer-guy/ed79064841f8619027b383bcf10333bc
<!--https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity-->
接下来,我们需要创建一个 GCP IAM 服务账户来映射 Kubernetes 服务账户,以便对 GCP 服务进行授权调用。配置Workload Identity包括使用 IAM 策略将 Kubernetes ServiceAccount 成员名称绑定到具有工作负载所需权限的 IAM 服务帐户。然后,来自使用此 Kubernetes ServiceAccount 工作负载的任何 Google Cloud API 调用都将作为绑定的 IAM 服务帐户进行身份验证。
当你在Namespace(命名空间)中配置 Kubernetes ServiceAccount 以使用Workload Identity时,IAM 将使用以下成员名称对凭证进行身份验证:
serviceAccount:PROJECT_ID.svc.id.goog[KUBERNETES_NAMESPACE/KUBERNETES_SERVICE_ACCOUNT]
让我们创建它:

https://gist.github.com/developer-guy/bd7f766d16e7971e20470e40d797720d
我们要确保 IAM 服务账户具有应用程序的角色。此外,在这种情况下,我们还可以授予它其他角色,例如 roles/cloudkms.viewer 和 roles/cloudkms.verifier。
<!--更多详情: cloud.google.com/kms/docs/reference/permissions-and-roles-->

网址 s://gist.github.com/developer-guy/6361cc3e6a8913d338e284a72e6ebd09
我们用必要的角色配置了 IAM 服务账户,并将其绑定到 kyverno 命名空间中名为 kyverno 的 Kubernetes ServiceAccount中。
接下来,我们将使用其 Helm 图表部署 Kyverno v1.6+。

https://gist.github.com/developer-guy/1e40f3bfe2e97c4773a59861c9f64dca
最后但并不是最不重要的一点,我们应该使用IAM服务账户的电子邮件地址注释Kubernetes ServiceAccount。

https://gist.github.com/developer-guy/9b7a9fd8f54033e923b3fcacd2e6bd11
现在是时候生成一个存储在GCP KMS中的Cosign密钥对了。

https://gist.github.com/developer-guy/3fa78dac357da4a6c418bb3be63b61da
让我们通过VerifyImages策略来测试所有这些内容。

https://gist.github.com/developer-guy/3596f939d233d1e41acad6deac99d881
<!--请注意,shape-shuttle-342907 是我们的 $PROJECT_ID 环境变量的值。-->
为了验证容器镜像,我们应该先对其进行签名。接下来,让我们开始操作:

https://gist.github.com/developer-guy/6ae836417d2438bac1149eaef789d94f
现在,让我们使用已签名的容器镜像运行 Pod。我们应该期待Kyverno 将会让我们创建这个Pod:

https://gist.github.com/developer-guy/3e21445119230a4e31081016eb525b7e
祝贺!你已经使用 KMS、Cosign 和 Workload Identity 验证了 Kyverno 的容器镜像!
原文链接:
https://blog.sigstore.dev/how-to-verify-container-images-with-kyverno-using-kms-cosign-and-workload-identity-1e07d2b85061