如何使用Podman签署和分发容器镜像

对容器镜像进行签名源于这样的动机:仅信任专用镜像提供商,以减轻中间人攻击(MITM)或针对容器注册表的攻击。签名镜像的一种方式是使用 GNU 隐私卫士(GPG)密钥。该技术通常兼容任何符合 OCI 标准的容器注册库(如 Quay.io)。值得一提的是,OpenShift 集成容器注册库开箱即支持此签名机制,从而无需单独存储签名。

对容器镜像进行签名源于这样的动机:仅信任专用镜像提供商,以减轻中间人攻击(MITM)或针对容器注册表的攻击。签名镜像的一种方式是使用 GNU 隐私卫士(GPG)密钥。该技术通常兼容任何符合 OCI 标准的容器注册库(如 Quay.io)。值得一提的是,OpenShift 集成容器注册库开箱即支持此签名机制,从而无需单独存储签名。

从技术角度来看,我们可以在将镜像推送到远程注册表之前使用Podman对其进行签名。之后,所有运行Podman的系统都必须配置为从远程服务器获取签名,该服务器可以是任何简单的Web服务器。这意味着在镜像拉取操作过程中,所有未签名的镜像都将被拒绝。但具体如何实现呢?

首先,我们需要创建一个GPG密钥对或选择本地已有的密钥。要生成新的GPG密钥,只需运行gpg --full-gen-key并按照交互式提示操作。现在我们应该能够验证该密钥是否存在于本地:

> gpg --list-keys sgrunert@suse.com
pub   rsa2048 2018-11-26 [SC] [expires: 2020-11-25]
      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid           [ultimate] Sascha Grunert <sgrunert@suse.com>
sub   rsa2048 2018-11-26 [E] [expires: 2020-11-25]

现在让我们假设我们运行着一个容器注册表。例如,我们可以简单地在本地机器上启动一个:

sudo podman run -d -p 5000:5000 docker.io/registry

注册表并不了解镜像签名机制,它仅为容器镜像提供远程存储服务。这意味着若需对镜像进行签名,我们必须自行处理签名的分发方式。

让我们选择标准的 Alpine 镜像进行签名实验:

sudo podman pull docker://docker.io/alpine:latest

sudo podman images alpine
REPOSITORY                 TAG      IMAGE ID       CREATED       SIZE
docker.io/library/alpine   latest   e7d92cdc71fe   6 weeks ago   5.86 MB

现在我们可以重新标记该镜像,使其指向我们的本地注册表:

sudo podman tag alpine localhost:5000/alpine

sudo podman images alpine
REPOSITORY                 TAG      IMAGE ID       CREATED       SIZE
localhost:5000/alpine      latest   e7d92cdc71fe   6 weeks ago   5.86 MB
docker.io/library/alpine   latest   e7d92cdc71fe   6 weeks ago   5.86 MB

现在 Podman 能够通过单条命令推送镜像并进行签名。但要实现这一功能,我们需要修改系统范围的注册表配置文件 /etc/containers/registries.d/default.yaml:

default-docker:
  sigstore: http://localhost:8000 # Added by us
  sigstore-staging: file:///var/lib/containers/sigstore

我们可以看到已配置了两个签名存储库:

  • sigstore: 引用一个用于读取签名的网络服务器

  • sigstore-staging: 引用一个用于写入签名的文件路径

现在,让我们推送并签名这个镜像:

sudo -E GNUPGHOME=$HOME/.gnupg \
    podman push \
    --tls-verify=false \
    --sign-by sgrunert@suse.com \
    localhost:5000/alpine
…
Storing signatures

如果我们现在查看系统的签名存储,会发现有一个新的签名可用,这是由镜像推送导致的:

sudo ls /var/lib/containers/sigstore
'alpine@sha256=e9b65ef660a3ff91d28cc50eba84f21798a6c5c39b4dd165047db49e84ae1fb9'

在我们编辑的 /etc/containers/registries.d/default.yaml 文件中,默认签名存储库引用了一个监听 http://localhost:8000的Web 服务器。为进行实验,我们只需在本地暂存签名存储库内启动一个新服务器:

sudo bash -c 'cd /var/lib/containers/sigstore && python3 -m http.server'
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

让我们为验证测试删除本地镜像:

sudo podman rmi docker.io/alpine localhost:5000/alpine

我们需要制定一项策略来强制要求签名必须有效。这可以通过在 /etc/containers/policy.json 中添加新规则来实现。请从下方示例中将“docker”条目复制到您 policy.json 文件的“transports”部分。

{
  "default": [{ "type": "insecureAcceptAnything" }],
  "transports": {
    "docker": {
      "localhost:5000": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/tmp/key.gpg"
        }
      ]
    }
  }
}

keyPath 目前尚不存在,因此我们必须将GPG密钥放在那里:

gpg --output /tmp/key.gpg --armor --export sgrunert@suse.com

如果我们现在拉取该镜像:

sudo podman pull --tls-verify=false localhost:5000/alpine
…
Storing signatures
e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a

然后我们可以在 Web 服务器的日志中看到签名已被访问:

127.0.0.1 - - [04/Mar/2020 11:18:21] "GET /alpine@sha256=e9b65ef660a3ff91d28cc50eba84f21798a6c5c39b4dd165047db49e84ae1fb9/signature-1 HTTP/1.1" 200 -

作为对应示例,如果我们在 /tmp/key.gpg 中指定了错误的密钥:

gpg --output /tmp/key.gpg --armor --export mail@saschagrunert.de
File '/tmp/key.gpg' exists. Overwrite? (y/N) y

那么就再也无法进行拉取操作了:

sudo podman pull --tls-verify=false localhost:5000/alpine
Trying to pull localhost:5000/alpine...
Error: pulling image "localhost:5000/alpine": unable to pull localhost:5000/alpine: unable to pull image: Source image rejected: Invalid GPG signature: …

因此,使用 Podman 和 GPG 对容器镜像进行签名时,通常需要考虑以下四个主要方面:

  1. 签名机器上需配置有效的私有 GPG 密钥,且所有将拉取镜像的系统都需部署对应的公钥

  2. 需在某处运行 Web 服务器,该服务器需具备访问签名存储库的权限

  3. 需在任意 /etc/containers/registries.d/*.yaml 配置文件中设置 Web 服务器

  4. 所有拉取镜像的系统都需通过 policy.conf 文件配置强制执行策略

关于镜像签名和GPG的设置就这些。值得一提的是,该方案同样能开箱即用支持 CRI-O,并可用于在 Kubernetes 环境中签名容器镜像。

  

所谓天才,只不过是把别人喝咖啡的功夫都用在工作上了。——鲁迅
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号