在过去的几年中,容器的普及率呈爆炸式增长。不幸的是,他们的安全风险也是如此。

目前可用的大多数容器都容易受到供应链攻击的攻击,因为它们只需一个简单的 API 密钥即可发布。如果该密钥泄漏,攻击者很容易发布一个看起来合法但实际上包含恶意软件的容器。

保护用户免受此类攻击的最佳方法之一是在创建时对容器映像进行签名,以便开发人员可以验证他们收到的是否是带有代码的真实映像.今天我们将了解如何自动签署我们的容器镜像,并将它们托管在 GitHub Container Registry 中。

视频

像往常一样,如果您是视觉学习者,或者只是喜欢看和听而不是阅读,这里有带有完整解释和演示的视频,公平地说,这是很多 more比这篇文章更完整

视频链接:https://youtu.be/OqZlKbTRWOY

如果您更喜欢阅读,那么......让我们继续吧:)

关于Sigstore和Cosign

所以,签署一个容器镜像。允许这样做的工具很少,但最令人兴奋的工具之一是 sigstore

Sigstore 是一个开源安全项目,现在由 OpenSSF(开放软件安全基金会)赞助,它允许开发人员安全地构建、分发和验证签名的软件工件

除此之外,sigstore 包含一个名为 cosign 的工具,可让您签署容器映像

[密钥类型](https://res.cloudinary.com/practicaldev/image/fetch/s--4AByqb2k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/dww3l9k3ptpl8d6cm7a4.png)

Cosign 支持几种类型的签名密钥,例如基于文本的密钥、基于云 KMS 的密钥甚至是在硬件令牌上生成的密钥,以及 kubernetes-secrets,这些都可以直接使用工具本身生成,并且还支持将 key-value annotations 添加到签名中(稍后我们将看到这一点)。

并且在您签署镜像之后,您需要一个支持签名镜像的 Container Registry,因为并非所有镜像都支持,即使支持签名镜像的那些也可能支持也可能不支持所有不同的签名。幸运的是,GitHub Container Registry 支持签名镜像,也支持 cosign。

说够了,让我们看看它如何与 GitHub Actions 和 GitHub Container 注册表一起工作。

协信安装

您需要做的第一件事是安装 cosign 以生成密钥。

您可以前往官方 GitHub 存储库 sigstore/cosign,单击 Releases,然后下载适用于您操作系统的版本。

[Cosign 发布](https://res.cloudinary.com/practicaldev/image/fetch/s--loHupbXb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/52amz76xa7bglxb5zlz9.png)

支持许多操作系统和平台,因此请务必选择正确的。

一旦你有了适合你的版本,你就可以运行它。重命名该工具也是可取的,在我的例子中,可执行文件名为cosign-windows-amd64.exe,但为了便于使用,我只重命名为cosign

密钥生成

现在您所要做的就是生成一个密钥。对于此示例,我将使用generate-key-pair命令生成一个静态文本密钥,该命令需要密码才能创建密钥。

密码可以通过环境变量给工具

set COSIGN_PASSWORD=123
cosign generate-key-pair

进入全屏模式 退出全屏模式

或带有交互式提示

cosign generate-key-pair

进入全屏模式 退出全屏模式

不幸的是,录制此视频时可用的最新版本有一个错误,如果您尝试使用交互式提示在 Windows 上提供密码,它会崩溃,如下所示。

[Cosign提示错误](https://res.cloudinary.com/practicaldev/image/fetch/s--NJDLcIw6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/iyv8a1zglvnwuq3m77gc.png)

如果您不想创建环境变量,可以使用 PowerShell 和您在下面看到的语法,并将您的密码通过管道传送到命令。

"myPassw0rd" | .\cosign.exe generate-key-pari

进入全屏模式 退出全屏模式

无论哪种情况,这都会为您创建私钥和公钥文件,您可以使用它们来签署和验证您的容器映像。

为 GitHub 生成 Cosign 密钥

但我们可以做得更好。正如我之前提到的,我想通过 GitHub Actions 对我的容器映像进行签名,所以现在我需要在 GitHub 中创建一些秘密,并将这些密钥复制到秘密值。但是工具可以直接为我们做到这一点!

例如,假设我想对 n3wt0n/SignedContainers 存储库中的图像进行签名。我可以使用相同的命令直接在 GitHub 中创建我的密钥:

export GITHUB_TOKEN=ghp_xyz123

export CONSIGN_PASSWORD=pwd123

./cosign generate-key-pair github://n3wt0n/SignedContainers

进入全屏模式 退出全屏模式

我需要做的第一件事是创建一个名为 GITHUB_TOKEN 的环境变量,它的值应该是对您的存储库具有写访问权限的 PAT。 (查看这个视频了解如何在 GitHub 中创建 PAT)

然后,我可以使用我们之前看到的命令来生成密钥,但是这次我们将 repo 作为输入参数传递。如您所见,语法是 github://OWNER/REPONAME

[共同签名秘密](https://res.cloudinary.com/practicaldev/image/fetch/s--NEL7M9hq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/4bcehcvy5q2tf0h4dqsc.png)

如您所见,包含我们指定的密码以及私钥和公钥的秘密是在我们的存储库中创建的,可以使用。

请记住,在某些情况下,在 GitHub 中创建了机密但其值为空,因此操作工作流会失败。我不确定为什么会发生这种情况,但如果您也发生这种情况,请告诉我。无论如何,解决方案很简单,只需尝试再次生成密钥,或者,如果问题仍然存在,请在本地生成密钥并将其手动复制到您的密钥中。

使用 Cosign 和 GitHub Actions 签署容器映像

好的,现在我们已经设置了密钥,让我们看看如何从 GitHub Actions 工作流程中对我们的图像进行签名

假设我们有一个相当标准的 Actions 工作流程,它只是构建一个 Docker 镜像并将其推送到 GitHub Container Registry(您可以在下面看到整个工作流程的 YAML)

我们要做的第一件事是安装 cosign。为此,我们可以使用预先构建的操作,只需搜索 cosign 即可找到 cosign 安装程序。它有几个参数,但它们是可选的,所以我们现在不需要它们:

    - name: cosign-installer
      uses: sigstore/cosign-installer@v2.0.0

进入全屏模式 退出全屏模式

当我们拥有它时,我们可以使用cosign sign命令对我们的镜像进行签名:

    - name: Sign the published Docker image
      env:
        COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
      run: cosign sign --key cosign.key ${{ env.REGISTRY }}/${{ github.actor }}/${{ env.IMAGE_NAME }}:${{ github.run_id }}

进入全屏模式 退出全屏模式

它使用 private key 进行签名,并且需要 cosign 密码才能访问它,当然我们还必须指定 full image name 以及注册表名称。

如您所见,命令需要他们的密钥才能在文件中,而我们目前在 GitHub 机密中拥有它。

解决方法是在签名命令之前添加另一个任务,该命令从密钥中读取密钥并将其写入磁盘:

    - name: Write signing key to disk
      run: 'echo "$KEY" > cosign.key'
      shell: bash
      env:
        KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}

进入全屏模式 退出全屏模式

___ 我不太喜欢它___,所以我希望有一个可以直接从密钥中读取它的 cosign 实现。

我们现在可以提交并运行我们的工作流程。几秒钟后,该过程完成,我们可以看到我们的图像已成功签名。

[容器镜像签名完成](https://res.cloudinary.com/practicaldev/image/fetch/s--O3Z3EJSz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/uploads/articles/gt4b3g3e4mzrp5iok4tm.png)

正如我所提到的,将密钥写入文件以将其提供给 cosign 的步骤是**相当一种解决方法**,它可能会带来一些安全风险,尤其是在自托管运行器上执行此操作时。一旦 Job 完成,托管的跑步者就会被处置,所以它“应该”没问题。

在更安全的场景中,比如企业场景,我建议将这些密钥保存在 Azure KeyVault、Hashicorp Vault、AWS KMS 或类似服务中,以避免此问题。

[Cosign 密钥提供者](https://res.cloudinary.com/practicaldev/image/fetch/s--bsUkq9d_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/03y2g8d2bppb1gd0v96y.png)

好消息是 cosign 支持直接从这些服务中读取密钥

如果您想查看完整的 YAML 工作流,请在 GitHub 上查看

验证容器镜像签名

无论如何,在图像被签名后,我们总是可以使用与私钥一起生成的公钥来验证它。您还可以与容器映像的开发人员和用户共享您的公钥,以便他们始终可以验证其真实性。

验证图像的真实性,我们可以使用cosign verify命令。

cosign verify --key cosign.pub ghcr.io/n3wt0n/signedcontainer:123

进入全屏模式 退出全屏模式

我们只需要将公钥文件和我们要验证的图像的名称传递给它,就是这样。

[签名验证](https://res.cloudinary.com/practicaldev/image/fetch/s--Q0DgaHyZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/b1z8adpjolgw3h2o8d1k.png)

如果为了比较,我们尝试验证一个没有用我们的密钥签名的图像,我们会得到这个错误:

[未找到签名](https://res.cloudinary.com/practicaldev/image/fetch/s--_6YxUa34--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/6wc8piz71hf0g7zh90td.png)

如果图像在我们签名后被更改或修改,当然也会发生这种情况,因此如果签名得到验证,我们的用户可以安全并信任图像

为签名添加注释

我们之前说过 cosign 还支持__在签名中添加key-value annotations__。让我们看看我们如何做到这一点。比方说,例如,我想对一张图片进行签名并添加一些 author 元数据 给它。

由于我这次是在本地运行,所以我需要先登录到容器注册表。

docker login -u myuser -p 123 ghcr.io

进入全屏模式 退出全屏模式

然后,我可以使用通常的命令 cosign sign,但这次我使用了一个 -a 标志,它代表注释,将一些键值对添加到我的图像中。

cosign sign --key cosign.key -a "author=CoderDave" ghcr.io/n3wt0n/signedcontainer:123

进入全屏模式 退出全屏模式

在本例中,我添加了 authoru003dCoderDave,但它可以是任何值,您可以添加多个值,也可以添加更多 -a 参数。

之后,我们可以像之前看到的那样使用cosign verify命令,它会显示我添加到图像中的注释

[键值注释](https://res.cloudinary.com/practicaldev/image/fetch/s--Cn2WWZCs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/uploads/articles/rewz58ifomnbnns429av.png)

注释功能可以非常有用。例如,如果您像我们之前看到的那样在 GitHub Actions 中运行签名过程,您可能希望将有关您的存储库、工作流运行等的信息添加到您的签名中,以使其更加完整。

[更多元数据](https://res.cloudinary.com/practicaldev/image/fetch/s--zSGYF6P1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to- uploads.s3.amazonaws.com/uploads/articles/acvax7sqq1nq7pn1nd9r.png)

Cosign 和 GitHub 操作:入门工作流程

正如我们所见,Cosign 及其流程运行良好,但设置它并不是很直接。好吧,再一次 GitHub 让它变得更简单 用于开始使用。

事实上,他们已经将 cosign 集成到了他们的入门工作流程中。只需转到 Actions > New Workflow,然后选择“Publish Docker Container”启动工作流。

[...]

      # Install the cosign tool except on PR
      # https://github.com/sigstore/cosign-installer
      - name: Install cosign
        if: github.event_name != 'pull_request'
        uses: sigstore/cosign-installer@1e95c1de343b5b0c23352d6417ee3e48d5bcd422
        with:
          cosign-release: 'v1.4.0'


    [...]

      # Sign the resulting Docker image digest except on PRs.
      # This will only write to the public Rekor transparency log when the Docker
      # repository is public to avoid leaking data.  If you would like to publish
      # transparency data even for private images, pass --force to cosign below.
      # https://github.com/sigstore/cosign
      - name: Sign the published Docker image
        if: ${{ github.event_name != 'pull_request' }}
        env:
          COSIGN_EXPERIMENTAL: "true"
        # This step uses the identity token to provision an ephemeral certificate
        # against the sigstore community Fulcio instance.
        run: cosign sign ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}

进入全屏模式 退出全屏模式

正如您在上面的摘录中所看到的,取自实际的启动器工作流程,其中没有 没有指定密钥

这是因为 Actions 支持 sigstore 中的其他 2 个工具:Fulcio,它是一个根 CA,从 OIDC 令牌颁发签名证书,以及 Rekor,一个由 Fulcio 颁发的证书的透明度日志.

多亏了这些,您可以在 Actions 中使用 GitHub 提供的 OIDC 令牌签署您的容器映像,而无需配置或管理您自己的私钥。

请务必注意,通过此无密钥签名流程,您的用户名、组织名称、存储库名称和工作流程名称将发布到 Rekor 公共透明度日志。这是公共存储库的正确选择,但可能不适用于私有存储库。事实上,GitHub 默认情况下已在私有存储库中禁用此功能,以防止潜在的泄漏。

结论

您如何看待对容器镜像进行签名,尤其是使用 cosign、GitHub Actions 和 GitHub Container Registry 进行签名?在下面的评论部分告诉我。

另外,请在此处查看这个视频,其中我有 3 个步骤让您更快地构建 Docker 映像。

喜欢、分享和关注我 🚀 了解更多内容:

📽YouTube

☕给我买杯咖啡

💖Patreon

📧通讯

🌐CoderDave.io 网站

👕商品

👦🏻脸书专页

🐱u200d💻GitHub

👲🏻推特

👴🏻领英

🔉播客

给我买杯咖啡

Logo

CI/CD社区为您提供最前沿的新闻资讯和知识内容

更多推荐