skopeoを使用したコンテナイメージへの署名

## 概要と環境

今回はskopeoを使って自身のコンテナイメージへの署名を行う。skopeoとはコンテナイメージやイメージリポジトリに対して様々な操作を実行できるCLIツール（[公式GitHub](https://github.com/containers/skopeo)）。今回コンテナイメージへの署名を行う環境は以下。

 <table>
    <tr>
      <td>OS</td>
      <td>mac OS Monterey</td>
    </tr>
    <tr>
      <td>CPU</td>
      <td>Apple M1 Pro</td>
    </tr>
    <tr>
      <td>Container Runtime</td>
      <td>Podman 4.1.1</td>
    </tr>
    <tr>
      <td>Container signing tools</td>
      <td>skopeo 1.8.0</td>
    </tr>
    <tr>
      <td>Signature Method</td>
      <td>PGP Signatures</td>
    </tr>
    <tr>
      <td>PGP Tool</td>
      <td>GnuPG 2.3.6</td>
    </tr>
    <tr>
      <td>Container Registry</td>
      <td>IBM Cloud Container Registry</td>
    </tr>
 </table>

## 署名手順

前提として以下ソフトウェアはインストール済みの想定で進めていく。

- Podman
- skopeo
- GnuPG


### 1. PGPキーペアの作成

```sh
> gpg --gen-key
```

この時メールアドレス、フルネーム、パスワードを要求される。メールアドレスはこの後のキーペア識別のプロセスで使用される。

確認

```sh
> gpg --list-keys 
gpg: 信用データベースの検査
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: 深さ: 0  有効性:   1  署名:   0  信用: 0-, 0q, 0n, 0m, 0f, 1u
gpg: 次回の信用データベース検査は、2024-06-29です
/Users/username/.gnupg/pubring.kbx
----------------------------------------
pub   ed25519 2022-06-30 [SC] [有効期限: 2024-06-29]
      ****************************************
uid           [  究極  ] username <user@domain>
sub   cv25519 2022-06-30 [E] [有効期限: 2024-06-29]
```

### 2. コンテナイメージへの署名

```sh
> skopeo copy --sign-by user@domain docker://jp.icr.io/namespace/sample:no-signed docker://jp.icr.io/namespace/sample:signed
```
引数とオプションについて説明する
 <table>
    <tr>
      <td>--sign-by オプション</td>
      <td>署名する際のkey ID。PGPキーペアの作成時のメールアドレスを指定</td>
    </tr>
    <tr>
      <td>第一引数</td>
      <td>未署名のコンテナイメージ。既存の未署名コンテナイメージを指定</td>
    </tr>
    <tr>
      <td>第二引数</td>
      <td>署名したコンテナイメージ。署名後に保存する場所と名前を指定</td>
    </tr>
 </table>

`skopeo copy`コマンドの詳細については[公式GitHub](https://github.com/containers/skopeo/blob/main/docs/skopeo-copy.1.md)を参照

以上でコンテナイメージへの署名ができる。

## 署名の確認

今回はローカルのPodmanに署名の検証を行う設定を、実際に署名済みのコンテナイメージをpullする。

### 1. 署名したメールアドレスのキーを配置する

```sh
> gpg --output /tmp/key.gpg --armor --export user@domain
```

### 2. Podmanのポリシーファイルを変更する

現在実行しているPodmanのmachineにsshする。

```sh
> podman machine ssh {machinename}
```

ssh完了後、ポリシーが記述されているファイルを変更する。

```sh
> sudo vi /etc/containers/policy.json
```

今回は`jp.icr.io`ドメインのコンテナレジストリにイメージを保管しているので、`jp.icr.io`からPullする際に署名の検証を行うように変更。
`keyPath`は配置したキーのパスを指定。（今回はskopeoをホストマシンで動かしているのでpodmanのmachineに対してvoumeをmountしており、そのパスを指定している）

```diff_shell
{
    "default": [
        {
            "type": "insecureAcceptAnything"
        }
    ],
    "transports": {
        "docker": {
            "registry.access.redhat.com": [
                {
                    "type": "signedBy",
                    "keyType": "GPGKeys",
                    "keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
                }
            ],
            "registry.redhat.io": [
                {
                    "type": "signedBy",
                    "keyType": "GPGKeys",
                    "keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
                }
            ],
+            "jp.icr.io": [
+                {
+                    "type": "signedBy",
+                    "keyType": "GPGKeys",
+                    "keyPath": "/mnt/Users/Documents/keys/key.gpg" 
+                }
+            ]
        },
        "docker-daemon": {
            "": [
                {
                    "type": "insecureAcceptAnything"
                }
            ]
        }
    }
}
```

### 3. 署名したcontainer imageのpull

正しいメールアドレスで署名したコンテナイメージの場合

```sh
> podman pull jp.icr.io/namespace/sample:singed                                                                                                                              
Trying to pull jp.icr.io/namespace/sample:singed...
Getting image source signatures
Checking if image destination supports signatures
Copying blob sha256:b2bba1b0d55c2bc59eff70dab9c3b780df30e642cf9a258abc77685b661b2591
Copying blob sha256:da62b97c2205cc3f43f9f9cb706083bc67f0c3a9ad6f549d926dda4bb7e6c5ee
Copying blob sha256:26a16869ff11ebd2eb902b15ce29e2b9f801e2b88cbfee212c153cdea8eafcfb
Copying blob sha256:25b4cabbd0308d844e766c2b31700653ddc6cb2650d54765374f1ddc2809058c
Copying blob sha256:e96f7b1dd1b96ad97646bbb6388be03e145b40c487eb849039ba8d9b975a575d
Copying blob sha256:2dd1cbe30bcd1a4b890eadbb94d7786961e9b6c84cd969c8db8f6767f127d849
Copying config sha256:8cfcd0fa92f4d4cd287c98ccc89729c7979c95b351e9f6594086ee14f0f93815
Writing manifest to image destination
Storing signatures
8cfcd0fa92f4d4cd287c98ccc89729c7979c95b351e9f6594086ee14f0f93815
```

間違ったメールアドレスで署名したコンテナイメージや未署名のコンテナイメージの場合

```sh
> podman pull jp.icr.io/namespace/sample:no-signed
Trying to pull jp.icr.io/namespace/sample:no-signed...
Error: Source image rejected: Signature for identity jp.icr.io/namespace/sample:singed is not accepted
```