# 在 Kubernetes 环境中检查镜像签名的一种方法

Kubernetes 的供应链安全需求中，有一个重要的镜像签署和校验的环节，这个环节可以使用 [OPA 结合 Notary](https://blog.fleeto.us/post/k8s-notary-and-opa/) 的方式来完成。最近 [Linux基金会宣布免费 sigstore 签名服务，以确认软件的来源和真实性](https://training.linuxfoundation.cn/news/208)，在[项目网站](https://sigstore.dev/)闲逛时，发现一个叫做 [cosign](https://github.com/sigstore/cosign) 的子项目，这是个轻量级的选择，让我非常有兴趣，于是就有了本文。

## 部署

目前这个工具还没有提供二进制发布，需要克隆源代码，并使用 **go 1.5** 进行构建，具体方法请参阅项目页面。简单说就是

```bash
# git clone https://github.com/sigstore/cosign.git
...
# cd cosign
# go build -o cosign ./cmd/cosign
...
```

这个工具的最基础功能有三个，分别是生成密钥对、镜像签名和校验签名。

## 生成密钥对

这个功能是很直白的：

```bash
cosign generate-key-pair
Enter password for private key:
Enter again:
Private key written to cosign.key
Public key written to cosign.pub
```

执行命令之后，输入密码，就会生成密钥对文件，私钥和公钥分别是 `consign.key` 和 `cosign.pub`。

## 签名

可以使用前边生成的密钥对进行签名，例如我的工具镜像：

```bash
cosign sign -key cosign.key dustise/sleep:v0.9.6
Enter password for private key:
Pushing signature to: index.docker.io/dustise/sleep:sha256-92dad62e00d08157a3921b7d7b568a247a8b24e8a067ad5dc20b210d7b1c2ad1.cosign
```

读者需要注意的一点是，这个功能是对**仓库中镜像的哈希码**生效的，因此签署过程无需本地镜像的参与，cosign 会直接在镜像仓库中获取对应 tag 的 sha256 内容，签署之后生成一个 OCI 镜像推送到该镜像的原有仓库之中，例如前面为 `dustise/sleep:v0.9.6` 进行签名，就生成了一个 `dustise/sleep:sha256-92da.....1c2ad1.cosign` 的镜像。如果被签名镜像在本地不存在，在完成操作之后，使用 `docker images` 命令查看，会发现被签署镜像和签署生成的镜像都不存在于本地。

另外一个就是，因为这里有 Push 操作，因此这个签署过程通常是有登录镜像库的需求的。

## 校验

校验过程很简单，使用 `verify` 指令，指定公钥即可，例如：

```bash
cosign verify  -key cosign.pub dustise/sleep:v0.9.6
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - The signatures were verified against the specified public key
  - Any certificates were verified against the Fulcio roots.
...
```

## 注意

如果使用 cosign 来进行签署，过程基本上来说还算是愉快的，私钥放置在 CI 之中，而公钥则可以保存在集群里，简单一点的方式，使用客户端定期扫描；复杂的方式，可以实现一个简单的 admission controller 来根据 Selector 对负载进行校验，同样需要注意的是，cosign 只针对远程（镜像库）进行操作，对本地的同 Tag 替换是没什么防御力的，因此这里还要使用 Always Pull 的策略进行弥补（可以使用 Kyverno 或者 Gatekeeper 来强制实施）。
