前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通过Kyverno使用KMS、Cosign和工作负载身份验证容器镜像

通过Kyverno使用KMS、Cosign和工作负载身份验证容器镜像

作者头像
CNCF
发布2022-04-19 09:50:20
4.8K0
发布2022-04-19 09:50:20
举报
文章被收录于专栏:CNCFCNCFCNCF

作者:developer-guy

随着软件供应链攻击的增加,保护我们的软件供应链变得更加重要。此外,在过去几年中,容器的采用也有所增加。有鉴于此,对容器镜像进行签名以帮助防止供应链攻击的需求日益增长。此外,我们今天使用的大多数容器,即使我们在生产环境中使用它们,也容易受到供应链攻击。在传统的 CI/CD 工作流中,我们构建镜像并将其推入注册中心。供应链安全的一个重要部分是我们构建的镜像的完整性,这意味着我们必须确保我们构建的镜像没有被篡改,这意味着保证我们从注册中心中提取的镜像与我们将要部署到生产系统中的镜像相同。证明镜像没有被篡改的最简单和最好的方法之一(多亏了 Sigstore)是在构建之后立即签名,并在允许它们部署到生产系统之前验证它。这就是 Cosign 和 Kyverno 发挥作用的地方。

Kyverno 是一个为 Kubernetes 设计的开源策略引擎,作为 Kubernetes 资源进行管理,不需要新的语言来编写策略。策略引擎是什么?它是一个软件,允许用户定义一组可以用来验证、改变(mutate)和生成 Kubernetes 资源的策略。作为 CNCF 的一个沙箱项目,Kyverno 开始得到社区的支持和关注。由于近年来软件供应链攻击的增加,Kyverno 越来越受欢迎。Kyverno 通过支持验证镜像签名[1]in-toto 证明[2]来保护工作负载。这些工作负载保护是通过cosign[3]SLSA[4]框架实现的。

用 Cosign 签名和验证

Cosign 是一个用于容器镜像签名和验证的工具,由 Sigstore 项目与 Linux 基金会合作维护。在众多特性中,Cosign 支持 KMS 签名、内置的二进制透明性、Rekor 提供时间戳服务以及 Kubernetes 策略执行。此外,Kyverno 利用 Cosign 来验证容器镜像签名、证明,等等。

软件工件通常是不透明的斑点,不容易进行安全检查,所以更常见的是推理它们是如何产生的,而不是它们里面有什么。我们不能将策略应用于单独的代码行,我们应用策略于谁构建了软件,他们是如何构建的,以及代码来自哪里。这种痕迹通常被称为一个软件的出处(provenance)。

如果你想获得更多关于出处和证明的细节,请参考来自 Dan Lorenc(@lorenc_dan)的这篇文章[5]

用户不是直接签署一个工件,而是创建一个文档来捕获他们签署工件背后的意图,以及作为这个签名一部分的任何特定声明。术语各不相同,但是由In-Toto[6]定义的分层模型似乎很有前途。

in-toto 证明格式[7]为元数据提供了一种灵活的方案,例如仓库和构建环境详细信息、漏洞扫描报告、测试结果、代码审查报告以及验证镜像完整性的其他信息。每个证明都包含一个带有 predicateType(谓词类型)和谓词(predicate)的签名语句。

从整体上考虑安全性并确保尽一切努力确保更高的安全性是一项挑战。SLSA[8]项目可以在这方面有所帮助。SLSA 代表“软件工件的供应链级别(Supply chain Levels for Software Artifacts)”,发音为“萨尔萨(salsa)”作为一个安全框架,你可以将其视为一个标准和控制的清单,以防止篡改,提高完整性,并保护你的项目、业务或企业中的包和基础结构。这是你如何做到足够安全,并在供应链的任何一个环节都尽可能有弹性。将 SLSA、Sigstore 和 Kyverno 结合在一起,可以为安全的软件开发生命周期提供坚实的基础。

现在我们已经介绍了 Kyverno 提供的供应链安全特性的基本部分,那么让我们深入了解一下它是如何在真实环境中实现所有这些特性的。

Kyverno 和使用工作负载身份的 Cosign

在下一部分,我们将在谷歌云平台(GCP)上使用谷歌 Kubernetes 引擎(GKE)和谷歌云密钥管理服务(KMS)等服务进行演示。正如在cosign[9]一节中提到的,云提供商的 KMS 系统是 Cosign 的一等公民,这意味着 Cosign 与 GCP KMS 能一起完美工作。

GCP KMS 是一种云服务,用于管理其他谷歌云服务的加密密钥,以便企业可以实现加密功能。云密钥管理服务允许你在单个集中式云服务中创建、导入和管理加密密钥并执行加密操作。

首先,我们需要在 GKE 上创建一个 Kubernetes 集群,并启用工作负载身份(Workload Identity)特性。但在此之前,我们还应该更多地了解工作负载身份,以及 Cosign 如何利用这一特性对 GCP 服务(如 GCP KMS)进行授权调用。

GCP 提供了工作负载身份特性,允许在 GKE 上运行的应用程序访问谷歌云 API,如计算引擎 API、BigQuery 存储 API 或机器学习 API。

工作负载身份[10]允许 GKE 集群中的 Kubernetes 服务帐户充当 IAM 服务帐户。当访问 Google Cloud API 时,使用已配置的 Kubernetes 服务帐户的 pod 会自动验证为 IAM 服务帐户。使用工作负载身份允许你为集群中的每个应用程序分配不同的、细粒度的身份和授权。

此外,对于运行在 Google Kubernetes Engine (GKE)上的工作负载,工作负载身份是以安全和可管理的方式访问 Google 云服务的推荐方式。幸运的是,我们不需要做任何额外的事情来在 GKE 上启用工作负载身份,因为 Cosign 可以通过提供环境凭据检测[11]功能支持来使用这个工作负载身份。再次感谢 Dan Lorenc,他写了另一篇精彩的博文来解释工作负载身份和环境凭证[12]之间的关系。

在我们的例子中,Kyverno 将在 GKE 上运行,因此我们将应用一个策略来验证容器镜像。Kyverno 中的这种类型的规则是verifyImages[13],如果在 OCI 注册中心中没有找到签名,或者如果镜像不是使用指定的密钥签名的,该规则将失败。如果还没有指定摘要(digest),它还会改变匹配的镜像以添加镜像摘要[14]。使用镜像摘要使得镜像引用不可变。

在上面的策略示例中,Kyverno 在内部使用 Cosign SDK 根据指定的密钥验证给定的镜像。假设我们使用 GCP KMS,Kyverno 必须通过该服务的认证才能正确调用 API。在这里,我们使用工作负载身份来实现这一点。

不是在你的代码旁边部署一个秘密,你的代码从环境中接收它需要的凭据。当然,这些必须来自某个地方——但是平台提供商现在管理存储、分发、刷新和撤销秘密的责任。你的应用程序可以直接从环境中按需读取环境凭据,而不是在构建/部署过程中提供长期机密(需要持续二进制文件运行的时间)。

在撰写本文时,Cosign 支持为四种不同的系统提供环境凭证检测,包括 GitHub、SPIFFE、Filesystem 和 Google。你可以在以下看到我们对 GCP KMS 的支持:

  • https://github.com/kyverno/kyverno/issues/2584
  • https://github.com/sigstore/sigstore/issues/138

有关更多细节,请查看 GitHub Cosign 仓库中的providers 目录[15]

演示

本节将运行上面描述的在 GKE 上运行 Kyverno 的演示,并使用一个策略来验证容器镜像。如果在 OCI 注册中心中找不到签名,或者签名不是使用指定的密钥签署的,此规则(verifyImages)将失败。如果还没有指定摘要,它还会改变匹配的镜像以添加镜像摘要。通过使用镜像摘要,我们的镜像引用是不可变的。

先决条件

  • kubectl v1.20+
  • gcloud v375.0.0
  • cosign v1.6.0

首先,我们需要在 GKE 上创建一个 Kubernetes 集群,并启用工作负载身份特性。我们将使用PROJECT_ID.svc.id.goog形式的固定工作负载身份池。

当你在集群上启用工作负载身份时,GKE 会自动为集群的 Google Cloud 项目创建一个固定的工作负载身份池。工作负载身份池允许 IAM 理解和信任 Kubernetes 服务帐户凭证。GKE 将该池用于项目中使用工作负载身份的所有集群。

$ export PROJECT_ID=$(gcloud config get-value project)
$ export CLUSTER_NAME="gke-wif"
$ gcloud container clusters create $CLUSTER_NAME \
 --workload-pool=$PROJECT_ID.svc.id.goog --num-nodes=2

https://gist.github.com/developer-guy/ed79064841f8619027b383bcf10333bc

https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity

接下来,我们需要创建一个 GCP IAM 服务帐户来映射一个 Kubernetes 服务帐户,以便对 GCP 服务进行授权呼叫。配置工作负载身份包括使用 IAM 策略将 Kubernetes ServiceAccount 成员名称绑定到具有工作负载所需权限的 IAM 服务帐户。然后,来自使用这个 Kubernetes ServiceAccount 的工作负载的任何 Google Cloud API 调用都被认证为绑定的 IAM 服务帐户。

当你在命名空间中配置 Kubernetes ServiceAccount 以使用工作负荷标识时,IAM 使用以下成员名验证身份证明:

serviceAccount:PROJECT_ID.svc.id.goog[KUBERNETES_NAMESPACE/KUBERNETES_SERVICE_ACCOUNT]

让我们创建它:

$ export GSA_NAME=kyverno-sa
$ gcloud iam service-accounts create $GSA_NAME
$ gcloud iam service-accounts add-iam-policy-binding \
 --role roles/iam.workloadIdentityUser \
 --member "serviceAccount:${PROJECT_ID}.svc.id.goog[kyverno/kyverno]" \
 ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

https://gist.github.com/developer-guy/bd7f766d16e7971e20470e40d797720d

我们确保 IAM 服务帐户拥有应用程序的角色。此外,我们还可以授予额外的角色,比如roles/cloudkms.viewerroles/cloudkms.verifier

更多详情见:https://cloud.google.com/kms/docs/reference/permissions-and-roles

$ gcloud projects add-iam-policy-binding ${PROJECT_ID} \
 --role roles/cloudkms.verifier \
 --member serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

$ gcloud projects add-iam-policy-binding ${PROJECT_ID} \
 --role roles/cloudkms.viewer \
 --member serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

https://gist.github.com/developer-guy/6361cc3e6a8913d338e284a72e6ebd09

我们为 IAM 服务帐户配置了必要的角色,并将其绑定到 kyverno 命名空间中名为 kyverno 的 Kubernetes ServiceAccount。

接下来,我们将使用 Kyverno 1.6+的 Helm chart 来部署它。

$ helm repo add kyverno https://kyverno.github.io/kyverno/
$ helm repo update
$ helm install kyverno kyverno/kyverno --namespace kyverno --create-namespace

https://gist.github.com/developer-guy/1e40f3bfe2e97c4773a59861c9f64dca

最后,我们应该用 IAM 服务帐户的电子邮件地址来注释 Kubernetes ServiceAccount。

$ kubectl annotate serviceaccount \
 --namespace kyverno \
 kyverno \
 iam.gke.io/gcp-service-account=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

https://gist.github.com/developer-guy/9b7a9fd8f54033e923b3fcacd2e6bd11

是时候用 Cosign 生成存储在 GCP KMS 的一个密钥对了。

$ gcloud kms keyrings create test - location "global"
$ gcloud kms keys create "cosign" \
 - location "global" \
 - keyring "test" \
 - purpose=asymmetric-signing - default-algorithm=ec-sign-p256-sha256
$ cosign generate-key-pair - kms gcpkms://projects/$PROJECT_ID/locations/global/keyRings/test/cryptoKeys/cosign/versions/1

https://gist.github.com/developer-guy/3fa78dac357da4a6c418bb3be63b61da

让我们通过应用 verifyImages 策略来测试所有这些。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-image
spec:
  validationFailureAction: enforce
  background: false
  webhookTimeoutSeconds: 30
  failurePolicy: Fail
  rules:
    - name: check-image
      match:
        resources:
          kinds:
            - Pod
      verifyImages:
      - image: "gcr.io/shaped-shuttle-342907/alpine:*"
        key: "gcpkms://projects/shaped-shuttle-342907/locations/global/keyRings/test/cryptoKeys/cosign/versions/1"

https://gist.github.com/developer-guy/3596f939d233d1e41acad6deac99d881

留意,shaped-shuttle-342907是我们的$PROJECT_ID 环境变量的值。

为了验证容器镜像,我们应该先签名。我们签字吧:

$ cosign sign --key gcpkms://projects/$PROJECT_ID/locations/global/keyRings/test/cryptoKeys/cosign/versions/1 gcr.io/$PROJECT_ID/alpine:3.15.0

https://gist.github.com/developer-guy/6ae836417d2438bac1149eaef789d94f

现在,让我们运行带有签名的容器镜像的 Pod。我们应该预期 Kyverno 会让我们创建这个 Pod:

$ kubectl run signed --image=gcr.io/$PROJECT_ID/alpine:3.15.0
pod/signed created

https://gist.github.com/developer-guy/3e21445119230a4e31081016eb525b7e

恭喜!你已经使用 KMS、Cosign 和工作负载身份用 Kyverno 验证了容器镜像!

参考资料

[1]验证镜像签名: https://kyverno.io/docs/writing-policies/verify-images/

[2]in-toto 证明: https://github.com/in-toto/attestation

[3]cosign: https://github.com/sigstore/cosign

[4]SLSA: https://slsa.dev/

[5]出处和证明: https://dlorenc.medium.com/policy-and-attestations-89650fd6f4fa

[6]In-Toto: https://in-toto.io/

[7]in-toto 证明格式: https://github.com/in-toto/attestation

[8]SLSA: https://slsa.dev/

[9]cosign: https://github.com/sigstore/cosign/blob/main/KMS.md

[10]工作负载身份: https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity

[11]环境凭据检测: https://dlorenc.medium.com/a-bit-of-ambiance-comes-to-sigstore-f80d1d6b1c30

[12]工作负载身份和环境凭证: https://dlorenc.medium.com/a-bit-of-ambiance-comes-to-sigstore-f80d1d6b1c30

[13]verifyImages: https://kyverno.io/docs/writing-policies/verify-images/

[14]镜像摘要: https://docs.docker.com/engine/reference/commandline/pull/#pull-an-image-by-digest-immutable-identifier

[15]providers 目录: https://github.com/sigstore/cosign/tree/main/pkg/providers


CNCF (Cloud Native Computing Foundation)成立于2015年12月,隶属于Linux Foundation,是非营利性组织。

CNCF(云原生计算基金会)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CNCF 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 用 Cosign 签名和验证
  • Kyverno 和使用工作负载身份的 Cosign
  • 演示
  • 先决条件
    • 参考资料
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档