Cosignでコンテナイメージに署名し、未署名のイメージがデプロイされないことを確認する


コンテナイメージに署名をすることで、第三者のイメージを誤って使ってしまうようなセキュリティインシデントを防ぐことが出来る。
今回、[cosign](https://docs.sigstore.dev/cosign/overview)を使ってコンテナイメージに署名をして、実際に署名がどのように働くかを確認する。
なお、ここではcosignが何かは取り扱わない。knqyf263さんの[sigstoreによるコンテナイメージやソフトウェアの署名](https://knqyf263.hatenablog.com/entry/2022/02/06/213003)という記事が非常に分かりやすかったので、cosignを知りたい方はこちらを読むのをオススメする。

## 前提
以下の条件を満たした状態で始める。
- クライアントPCでDocker Daemonが起動済み
- Kubernetesが構築済み
- Harborが構築済み

Harborは署名が効いているかを確認するために利用する。ない人はDockerHubなどを使ってもらっても構わないが、一部の手順が変わる可能性がある点に注意。

## 署名の適用

cosignをインストールする。
[公式サイト](https://docs.sigstore.dev/cosign/installation/)からインストール手順を取得したが、シンボリックリンクを張る作業が追加で必要だったので注意。
OSごとにインストール手順が用意されているので、Debian系以外の人はそれぞれの手順を参照してインストールする必要がある。
```
wget "https://github.com/sigstore/cosign/releases/download/v1.6.0/cosign_1.6.0_amd64.deb"
sudo dpkg -i "cosign_1.6.0_amd64.deb"
sudo ln -s /usr/local/bin/cosign-linux-amd64 /usr/local/bin/cosign
```

cosignで署名するためには事前にキーペアを作っておく必要がある。\
ここではパスワードを`hogefuga`として進めた。
```
$ cosign generate-key-pair
Enter password for private key:
Enter password for private key again:
Private key written to cosign.key
Public key written to cosign.pub
```
実行後、秘密鍵と公開鍵がカレントディレクトリに作成される。
```
$ ls
cosign.key  cosign.pub
```
作成したキーペアを使ってコンテナイメージに署名する。
```
cosign sign --key cosign.key myharbor.info/myapp/mynginx
```
コマンド実行前のHarborでは、以下のように"Signed by Cosign"の項目が✕になっていたが、
![1669014999318.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/8b51b021-4f18-ca54-7d62-1cfe1b3c5d78.png)

コマンド実行後は以下のようにチェックマークが表示されるようになった。
![1669015038366.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/04d6c096-7edf-a5a5-ca76-71ba2a24a308.png)

## 署名の効果の確認
HarborのProjectでConfigurationを選択すると、以下のように"Deployment security"に署名方法としてCosignとNotaryが表示されている。
![1669015147536.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/4bcfb747-b78d-54ab-3bab-8b40b67298f7.png)
ここでチェックを入れると、チェックした方式で署名されたイメージしかデプロイできなくなる。
ここではCosignにチェックを入れて下の"SAVE"ボタンを押して設定を保存する。

署名していないイメージのテスト用にdockerhubのnginxを自前のHarborにコピーする。
```
docker pull nginx
docker tag nginx myharbor.info/myapp/nginx-not-signed
docker push myharbor.info/myapp/nginx-not-signed
```
こちらは署名していないので以下の表示となる。
![1669015790797.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/dc7d8a17-4bca-947f-3db6-6557e1b7a730.png)


それぞれのイメージを使ったPodを起動してみる。
```
kubectl run --image myharbor.info/myapp/nginx-not-signed nginx-not-signed
kubectl run --image myharbor.info/myapp/mynginx mynginx
```
署名していない方のイメージを使ったPodだけ起動に失敗していることが分かる。
```
$ kubectl get pod
NAME               READY   STATUS             RESTARTS   AGE
mynginx            1/1     Running            0          81s
nginx-not-signed   0/1     ImagePullBackOff   0          72s
```
エラーの詳細を見ると、以下のようになっている。
```
$ kubectl describe pod nginx-not-signed
：（省略）
  Warning  Failed     17s (x4 over 111s)  kubelet            Failed to pull image "myharbor.info/myapp/nginx-not-signed": rpc error: code = Unknown desc = failed to pull and unpack image "myharbor.info/myapp/nginx-not-signed:latest": failed to resolve reference "myharbor.info/myapp/nginx-not-signed:latest": pulling from host myharbor.info failed with status code [manifests latest]: 412 Precondition Failed
```
未署名の場合はエラーコード412を返すようだ。