Docker 中的内容信任

在网络化系统之间传输数据时,信任是核心关注点。尤其是在通过互联网等不受信任的介质通信时,确保系统操作的所有数据的完整性和发布者至关重要。您可以使用 Docker Engine 向公共或私有 registry 推送和拉取镜像(数据)。内容信任使您能够验证通过任何渠道从 registry 接收的所有数据的完整性和发布者。

关于 Docker 内容信任 (DCT)

Docker 内容信任 (DCT) 提供了对发送到远程 Docker registry 以及从其接收的数据使用数字签名的能力。这些签名允许在客户端或运行时验证特定镜像标签的完整性和发布者。

通过 DCT,镜像发布者可以签署其镜像,而镜像消费者可以确保他们拉取的镜像已签署。发布者可以是手动签署内容的个人或组织,也可以是作为发布过程一部分自动签署内容的软件供应链。

镜像标签与 DCT

单个镜像记录具有以下标识符

[REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]

一个特定的镜像 仓库 (REPOSITORY) 可以有多个标签 (tags)。例如,latest3.1.2 都是 mongo 镜像的标签。镜像发布者可以多次构建镜像和标签的组合,每次构建都会更改镜像。

DCT 与镜像的 标签 (TAG) 部分关联。每个镜像仓库都有一组密钥,镜像发布者使用这些密钥签署镜像标签。镜像发布者可以自行决定签署哪些标签。

一个镜像仓库可以包含一个已签署标签的镜像和另一个未签署标签的镜像。例如,考虑Mongo 镜像仓库latest 标签可能是未签署的,而 3.1.6 标签可能是已签署的。决定镜像标签是否签署是镜像发布者的责任。在此表示中,一些镜像标签已签署,而另一些则未签署

Signed tags

发布者可以选择签署或不签署特定标签。因此,未签署标签的内容与同名的已签署标签的内容可能不一致。例如,发布者可以推送带有标签 someimage:latest 的镜像并签署它。稍后,同一发布者可以推送一个未签署的 someimage:latest 镜像。第二次推送会替换上一个未签署的 latest 标签,但不会影响已签署的 latest 版本。可以选择签署哪些标签的能力,允许发布者在正式签署镜像的未签署版本之前进行迭代。

镜像消费者可以启用 DCT 来确保他们使用的镜像是已签署的。如果消费者启用 DCT,他们只能拉取、运行或使用可信镜像进行构建。启用 DCT 有点像为您的 registry 应用“过滤器”。消费者“看到”的只是已签署的镜像标签,而那些不太理想的、未签署的镜像标签对他们来说是“不可见的”。

Trust view

对于未启用 DCT 的消费者,他们使用 Docker 镜像的方式没有任何变化。无论是否签署,所有镜像都可见。

Docker 内容信任密钥

镜像标签的信任通过使用签署密钥进行管理。首次调用使用 DCT 的操作时会创建一组密钥。密钥集包括以下类别的密钥

  • 一个离线密钥,它是镜像标签 DCT 的根
  • 仓库或标签密钥,用于签署标签
  • 服务器管理的密钥,例如时间戳密钥,它为您的仓库提供新鲜度安全保证

下图描绘了各种签署密钥及其关系

Content Trust components

警告

根密钥一旦丢失将无法恢复。如果您丢失任何其他密钥,请发送电子邮件至Docker Hub 支持。此丢失还需要在此丢失之前使用此仓库已签署标签的每个消费者进行手动干预。

您应该将根密钥备份到安全的地方。考虑到它仅用于创建新的仓库,最好将其离线存储在硬件中。有关如何保护和备份密钥的详细信息,请务必阅读如何管理 DCT 密钥

使用 Docker 内容信任签署镜像

在 Docker CLI 中,我们可以使用 $ docker trust 命令语法签署和推送容器镜像。这是构建在 Notary 功能集之上的。有关更多信息,请参阅Notary GitHub 仓库

签署镜像的先决条件是连接了 Notary 服务器的 Docker Registry(例如 Docker Hub)。有关搭建自托管环境的说明,请参见此处

要签署 Docker 镜像,您需要一对授权密钥。这些密钥可以使用 $ docker trust key generate 在本地生成,或者由证书颁发机构生成。

首先,我们将授权私钥添加到本地 Docker 信任仓库中。(默认情况下,它存储在 ~/.docker/trust/ 中)。如果您使用 $ docker trust key generate 生成授权密钥,则私钥会自动添加到本地信任存储中。如果您要导入单独的密钥,则需要使用 $ docker trust key load 命令。

$ docker trust key generate jeff
Generating key for jeff...
Enter passphrase for new jeff key with ID 9deed25:
Repeat passphrase for new jeff key with ID 9deed25:
Successfully generated and loaded private key. Corresponding public key available: /home/ubuntu/Documents/mytrustdir/jeff.pub

或者如果您有现有密钥

$ docker trust key load key.pem --name jeff
Loading key from "key.pem"...
Enter passphrase for new jeff key with ID 8ae710e:
Repeat passphrase for new jeff key with ID 8ae710e:
Successfully imported key from key.pem

接下来,我们需要将授权公钥添加到 Notary 服务器;这对于 Notary 中的特定镜像仓库(称为 Global Unique Name (GUN))是特定的。如果您是第一次将授权添加到该仓库,此命令还将使用本地 Notary 规范根密钥初始化该仓库。要了解更多关于初始化仓库和授权作用的信息,请访问内容信任授权

$ docker trust signer add --key cert.pem jeff registry.example.com/admin/demo
Adding signer "jeff" to registry.example.com/admin/demo...
Enter passphrase for new repository key with ID 10b5e94:

最后,我们将使用授权私钥签署特定标签并将其推送到 registry。

$ docker trust sign registry.example.com/admin/demo:1
Signing and pushing trust data for local image registry.example.com/admin/demo:1, may overwrite remote trust data
The push refers to repository [registry.example.com/admin/demo]
7bff100f35cb: Pushed
1: digest: sha256:3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e size: 528
Signing and pushing trust metadata
Enter passphrase for signer key with ID 8ae710e:
Successfully signed registry.example.com/admin/demo:1

另外,导入密钥后,可以通过导出 DCT 环境变量来使用 $ docker push 命令推送镜像。

$ export DOCKER_CONTENT_TRUST=1

$ docker push registry.example.com/admin/demo:1
The push refers to repository [registry.example.com/admin/demo:1]
7bff100f35cb: Pushed
1: digest: sha256:3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e size: 528
Signing and pushing trust metadata
Enter passphrase for signer key with ID 8ae710e:
Successfully signed registry.example.com/admin/demo:1

可以使用 $ docker trust inspect 命令查看标签或仓库的远程信任数据

$ docker trust inspect --pretty registry.example.com/admin/demo:1

Signatures for registry.example.com/admin/demo:1

SIGNED TAG          DIGEST                                                             SIGNERS
1                   3d2e482b82608d153a374df3357c0291589a61cc194ec4a9ca2381073a17f58e   jeff

List of signers and their keys for registry.example.com/admin/demo:1

SIGNER              KEYS
jeff                8ae710e3ba82

Administrative keys for registry.example.com/admin/demo:1

  Repository Key:	10b5e94c916a0977471cc08fa56c1a5679819b2005ba6a257aa78ce76d3a1e27
  Root Key:	84ca6e4416416d78c4597e754f38517bea95ab427e5f95871f90d460573071fc

可以使用 $ docker trust revoke 命令移除标签的远程信任数据

$ docker trust revoke registry.example.com/admin/demo:1
Enter passphrase for signer key with ID 8ae710e:
Successfully deleted signature for registry.example.com/admin/demo:1

使用 Docker 内容信任进行客户端强制执行

内容信任在 Docker Client 中默认是禁用的。要启用它,请将 DOCKER_CONTENT_TRUST 环境变量设置为 1。这会阻止用户使用带有标签的镜像,除非它们包含签名。

在 Docker client 中启用 DCT 后,操作带有标签镜像的 docker CLI 命令必须具有内容签名或显式内容哈希。与 DCT 相关的命令有

  • push
  • build
  • create
  • pull
  • run

例如,启用 DCT 后,只有当 someimage:latest 被签署时,docker pull someimage:latest 才会成功。然而,只要哈希存在,带有显式内容哈希的操作总是会成功

$ docker pull registry.example.com/user/image:1
Error: remote trust data does not exist for registry.example.com/user/image: registry.example.com does not have trust data for registry.example.com/user/image

$ docker pull registry.example.com/user/image@sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a
sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1: Pulling from user/image
ff3a5c916c92: Pull complete
a59a168caba3: Pull complete
Digest: sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1
Status: Downloaded newer image for registry.example.com/user/image@sha256:ee7491c9c31db1ffb7673d91e9fac5d6354a89d0e97408567e09df069a1687c1
页面选项