OpenShiftの内部レジストリのイメージ署名検証機能を使ってみる

# はじめに

今回取り上げるトピックは前後編で記事を分けています。この記事は前編になります。
前編：[OpenShiftの内部レジストリのイメージ署名検証機能を使ってみる(当記事)](https://qiita.com/shin7446/items/99d01e54600899d88090)
後編：[OpenShiftの内部レジストリの署名検証機能を使ってOpenShiftクラスター間のイメージ署名＆検証を実現する](https://qiita.com/shin7446/items/ceedd3fcff5baa2ebdf6)


OpenShiftではデフォルトのコンポーネントとしてクラスター上にコンテナイメージ用のレジストリが具備されています。
このレジストリはQuayやHarborなどのように充実した機能を具備している訳ではありませんが、認証機能をOpenShiftのRBACと統合できたり、なによりクラスター構築時に自動で構成されるのですぐ使えるということもあり、OpenShiftの使い始めはこのレジストリを使って開発をスタートする人も多いかと思います。

実はこのレジストリはイメージの署名検証の機能も具備しており、イメージをPullしたタイミングでそのイメージの署名情報を自動的に検証し、イメージの真正性を担保することができます。

この機能の概要や使い方はOpenShift Container Platformの公式ドキュメントに記載がありますが、いざみてみると何やっているのかよくわからなかったので、ドキュメントの手順をなぞりながら、自分なりの理解をまとめていきます。

# 前提条件
この内容はOpenShift Container Platform 4.10を対象としています。

# ドキュメントの手順の概要
OpenShiftのドキュメントでは内部レジストリのイメージ署名を以下の２ページで説明しています。
* [Red Hat Container Registry の署名の検証の有効化](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.10/html/security_and_compliance/security-container-signature#containers-signature-verify-enable_security-container-signature)
* [署名の検証設定の確認](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.10/html/security_and_compliance/security-container-signature#containers-signature-verify-enable_security-container-signature)

内容としては以下の通りです。
* Red Hat Container Registry の署名の検証の有効化
* policy.jsonをノードに格納するためのMachineConfigを作成
* MachineConfigのデプロイ
* 各Workerノードに`/etc/containers/registries.d/registry.redhat.io.yaml`と`/etc/containers/registries.d/registry.access.redhat.com.yaml`を作成
* MachineConfigの適用状態の確認
* 各ファイルの格納状況の確認
    * `policy.json`
    * `registry.redhat.io.yaml`
    * `registry.access.redhat.com.yaml`

...ということで、だいぶ簡素な手順に仕上がっております。
これをやっても以下のような疑問が浮かんでくるかと思います。
* 結局これなにやってるの？
* ほんとにこれでイメージ署名の検証ができてるの？
* 検証に失敗したらどうなるの？
* これはRed Hatが配布するイメージにしか検証してないけど、自分のレジストリで実行するにはどうすればいいの？

そこで今回は`これ何やってるの？`と`ほんとにこれでイメージ署名の検証ができてるの？`あたりを解決できればと思います。

# Red Hatにおけるイメージ検証の仕組み
Red Hatでは、GnuPGの鍵を使ったイメージの署名／検証を採用しています。この仕組みの概要や、他の署名方式については以下の記事にわかりやすくまとまっています。

https://qiita.com/Yuhkih/items/fbad089046bfcf8d824a

Red Hatのレジストリに格納されているイメージに対する署名および検証の仕組みは以下のような形になっています。

![署名・検証フロー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419286/5550a46c-05eb-f4c4-77df-f1f805e2d647.png)

Red Hatはイメージに署名し、GnuPGの鍵で暗号化した署名情報をそれぞれのレジストリ/署名ストアに格納します。
そしてイメージ利用者はイメージをpullする際に、一緒に署名ストアから秘密鍵で暗号化された署名情報を取得し、公開鍵で復号してダイジェスト(`sha256:xxxx....`)を取得します。
その後このダイジェストとコンテナイメージのダイジェストを比較し、一致するかを確認（検証）します。

上の図の①〜⑤まではRed Hatが実施しています。今回のOpenShiftの内部レジストリにて行う設定は、この⑤〜⑨を実行するために行います。

では順番にドキュメントを手順を見ていきましょう。

# ドキュメント手順

まずButane設定ファイルを作成しています。

```51-worker-rh-registry-trust.bu
variant: openshift
version: 4.10.0
metadata:
  name: 51-worker-rh-registry-trust
  labels:
    machineconfiguration.openshift.io/role: worker
storage:
  files:
  - path: /etc/containers/policy.json
    mode: 0644
    overwrite: true
    contents:
      inline: |
        {
          "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"
                }
              ]
            },
            "docker-daemon": {
              "": [
                {
                  "type": "insecureAcceptAnything"
                }
              ]
            }
          }
        }
```

その後、`MachineConfig`用のyamlファイルを生成しています。
```
butane 51-worker-rh-registry-trust.bu -o 51-worker-rh-registry-trust.yaml
```
できたyamlファイルは以下の通りです。

```51-worker-rh-registry-trust.yaml
# Generated by Butane; do not edit
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  labels:
    machineconfiguration.openshift.io/role: worker
  name: 51-worker-rh-registry-trust
spec:
  config:
    ignition:
      version: 3.2.0
    storage:
      files:
        - contents:
            compression: gzip
            source: data:;base64,H4sIAAAAAAAC/8ySMWvDMBCFd/2K42bL3r2lUDyEgildSskgpKssHEtCugwi+L8XJ8a4pLRrBwmJd7z7jndXAYCGPtXlzNjChwAAuN5uAOQSCVtA5zPpS6KD1hT54AsPzlu8lc0C4FQtNpyUzzEkztiuHmiCHiltfwBMZF3mVGqlNeVcJzKD4lqHaeu/Z/jGkZ31ZJ4KVnt1pPK2FnR9d6SSH/Re8bDoDbFu4uiaFCdpo21e+xfZ9Z08Pr/LO4lMdCaVCTeLeX2dqocZVngX/j37Patqn4o0iqbg9+H8PcZvq/BDQ7GcWXwFAAD//2KyDxpnAgAA
          mode: 420
          overwrite: true
          path: /etc/containers/policy.json
```

この`MachineConfig`は、butaneファイルに記載している`policy.json`を`/etc/containers/policy.json`として各Workerノードに配置するためのものです。
ではここで`policy.json`の中身を抜粋してみます。

```
{
  "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"
        }
      ]
    },
    "docker-daemon": {
      "": [
        {
          "type": "insecureAcceptAnything"
        }
      ]
    }
  }
}
```

このpolicy.jsonの使い方は[こちら](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)に記載があります。
`defaut.type`で"insecureAcceptAnything"とあるため、基本的には署名の検証を行わずOKとする形になりますが、今回は`transports`にRed Hatのレジストリの情報が記載されているため、ここのレジストリからpullするイメージには署名検証を実施することになります。
では`registry.redhat.io`の設定を見てみましょう。
```
      "registry.redhat.io": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
        }
      ]
```
この記述で、`registry.redhat.io`から取得するイメージに対して署名検証を行う設定を行なっています。
そして署名情報を復号する際に使う公開鍵を指定しています。ここから`/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release`を使って復号することがわかります。

あれ？この公開鍵はいつ手に入れるの？となるかもしれませんが、OpenShiftで使うノードのOSイメージにはこの公開鍵がデフォルトで格納されています。よって上のフローにおける`⑤公開鍵の取得`は気にしなくてOKです。

ちなみにデフォルトの環境ではこのpolicy.jsonはどうなっているのでしょう？実際にWorkerノードにアクセスして確認してみてみましょう。
```
$ oc debug node/<ノード名> -- chroot /host cat /etc/containers/policy.json
{
    "default": [
        {
            "type": "insecureAcceptAnything"
        }
    ],
    "transports":
        {
            "docker-daemon":
                {
                    "": [{"type":"insecureAcceptAnything"}]
                }
        }
}
```

このように、現在は`transports`に`registry.redhat.io`の記載がないので、デフォルトの設定が反映されてイメージ署名検証を行わない(`insecureAcceptAnything`)設定になっています。

ではドキュメントの手順に戻り、この`MachineConfig`をデプロイします。
```
$ oc apply -f 51-worker-rh-registry-trust.yaml
```

`MachineConfig`がデプロイされると、この情報を反映するためにWorkerノードの更新が走ります。
更新の確認についてはドキュメントの通りなのでここでは割愛します。

しばらくすると`MachineConfig`更新が完了するので、あらためてWorkerノードにログインして確認してみましょう。
```
$ oc debug node/<ノード名> -- chroot /host cat /etc/containers/policy.json
{
  "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"
        }
      ]
    },
    "docker-daemon": {
      "": [
        {
          "type": "insecureAcceptAnything"
        }
      ]
    }
  }
}
```

確認すると、実際にpolicy.jsonの中身が更新されていることがわかります。

次にドキュメントでは`registry.redhat.io.yaml`と`registry.access.redhat.com.yaml`を作成していますが、IPIインストールした環境ではデフォルトで存在するためこの手順は不要です。
一応中身を確認してみます。
```
$ oc debug node/<ノード名> -- chroot /host cat /etc/containers/registries.d/registry.redhat.io.yaml
docker:
     registry.redhat.io:
         sigstore: https://registry.redhat.io/containers/sigstore
```
このファイルの詳細は[こちら](https://github.com/containers/image/blob/main/docs/containers-registries.d.5.md)に記載があります。
ここでは、`registry.redhat.io`から取得するイメージの署名情報を`https://registry.redhat.io/containers/sigstore`から取得するように指定しています。
要はRed Hatは暗号化したイメージの署名情報の格納先として、このURLの署名ストアを使っているようです。
※同様に`registry.access.redhat.com.yaml`も署名ストアが記載されています。


以上が上のフローにおける⑥〜⑨を実現するための設定になります。
まとめるとこんな感じです。
* ⑥イメージの取得：
    * `registry.redhat.io`は`/etc/containers/policy.json`で`transports`設定されているので、署名検証が必要になる
* ⑦署名情報の取得：
    * `/etc/containers/registries.d/registry.redhat.io.yaml`で指定された署名ストアから署名情報を取得する
* ⑧復号：
    * `/etc/pki/rpm-gpg`にある公開鍵`RPM-GPG-KEY-redhat-release`を使って署名情報を復号する
* ⑨検証：
    * 復号したダイジェストとイメージを比較する

# ほんとにこれでイメージ署名の検証ができてるの？
さて、ドキュメント上ではこれで署名検証の設定ができたとありますが、本当に検証しているのでしょうか？
確認する上で、`registry.redhat.io`上に署名されていないイメージがあると楽だったのですが、軽く調べた限りでは署名のないイメージが見当たりませんでした。。。

そのため今回は以下のような方法で検証してみたいと思います。
* `RPM-GPG-KEY-redhat-release`とは別の公開鍵を使ってみる
* 取得する署名ストアを間違えてみる

## 別の公開鍵を使ってみる
別の公開鍵を使うために、ローカルで適当な鍵を作って、その鍵をOpenShiftのノードにアップします。
まずは鍵を作成します。
```
$ gpg --gen-key

...omit...

Real name: testtest
Email address: test@sample.com
You selected this USER-ID:
    "testtest <test@sample.com>"

Change (N)ame, (E)mail, or (O)kay/(Q)uit? O

...omit...

pub   rsa3072 2022-06-12 [SC] [expires: 2024-06-11]
      20AF23FF23B23A41104C6883036BE9A1D7566690
uid                      testtest <test@sample.com>
sub   rsa3072 2022-06-12 [E] [expires: 2024-06-11]
```
作成した鍵の公開鍵を取得します。
```
$ gpg -ao test.pub --export testtest
```

公開鍵ができました。　
```
$ cat test.pub
-----BEGIN PGP PUBLIC KEY BLOCK-----

...omit...

-----END PGP PUBLIC KEY BLOCK-----
```

では`registry.redhat.io`のイメージの署名情報を、この公開鍵で復号するように設定していきます。
まずpolicy.jsonの中身を編集したいので、`51-worker-rh-registry-trust.bu`を以下のように修正していきます。
```51-worker-rh-registry-trust.bu
variant: openshift
version: 4.10.0
metadata:
  name: 51-worker-rh-registry-trust
  labels:
    machineconfiguration.openshift.io/role: worker
storage:
  files:
  - path: /etc/containers/policy.json
    mode: 0644
    overwrite: true
    contents:
      inline: |
        {
          "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/test.pub"
                }
              ]
            },
            "docker-daemon": {
              "": [
                {
                  "type": "insecureAcceptAnything"
                }
              ]
            }
          }
        }
  - path: /etc/pki/test.pub
    mode: 0644
    overwrite: true
    contents:
      inline: |
        -----BEGIN PGP PUBLIC KEY BLOCK-----

        ...omit...

        -----END PGP PUBLIC KEY BLOCK-----
```
みてわかる通り、`registry.redhat.io`の`keyPath`を`/etc/pki/test.pub`に変更しています。
また、ノードに`/etc/pki/test.pub`を格納するために公開鍵の情報を追記しました。

では`MachineConfig`のyamlファイルを生成します。

```
$ butane 51-worker-rh-registry-trust.bu -o 51-worker-rh-registry-trust-test.yaml
```

applyしていきます。
```
$ oc apply -f 51-worker-rh-registry-trust-test.yaml
```

しばらく待って、ノードの状態を確認しましょう。
```
$ oc debug node/<ノード名> -- chroot /host cat /etc/containers/policy.json
{
  "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/test.pub"
        }
      ]
    },
    "docker-daemon": {
      "": [
        {
          "type": "insecureAcceptAnything"
        }
      ]
    }
  }
}

$ oc debug node/<ノード名> -- chroot /host cat /etc/pki/test.pub
-----BEGIN PGP PUBLIC KEY BLOCK-----

...omit...

-----END PGP PUBLIC KEY BLOCK-----
```

これでダミーの公開鍵を使う設定になりました。

では実際にデプロイしてみましょう。
まずpod.yamlを作成します。
今回は[hello-openshift](https://catalog.redhat.com/software/containers/openshift4/ose-hello-openshift-rhel8/5f7f1f8dd8bb1c6806f2f0ba)イメージを使います。
```pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-openshift
spec:
    containers:
      - name: web
        image: registry.redhat.io/openshift4/ose-hello-openshift-rhel8:v4.7
```

実際にこのpodをデプロイしてみましょう。

```
$ oc new-project test-project
$ oc apply -f pod.yaml
```

イメージはこのタイミングでPullされるので、実際にデプロイできているか確認してみましょう。
```
$ oc get pods
NAME                                         READY   STATUS         RESTARTS   AGE
ose-hello-openshift-rhel8-56cbcc5546-72kck   0/1     ErrImagePull   0          19s
```

ということで、`ErrImagePull`になりました。Podのイベントを確認してみましょう。

```
$ oc describe pods

...omit...

Events:
  Type     Reason          Age                  From               Message
  ----     ------          ----                 ----               -------
  Normal   Scheduled       2m14s                default-scheduler  Successfully assigned test-project/hello-openshift to ip-10-0-184-16.ap-northeast-1.compute.internal
  Normal   AddedInterface  2m13s                multus             Add eth0 [10.131.0.18/23] from openshift-sdn
  Normal   Pulling         25s (x4 over 2m13s)  kubelet            Pulling image "registry.redhat.io/openshift4/ose-hello-openshift-rhel8@sha256:ee88016114c97dae2849195f1b0e94f65bd6cfe823ce569b9b0c6097296cdcf0"
  Warning  Failed          23s (x4 over 2m6s)   kubelet            Failed to pull image "registry.redhat.io/openshift4/ose-hello-openshift-rhel8@sha256:ee88016114c97dae2849195f1b0e94f65bd6cfe823ce569b9b0c6097296cdcf0": rpc error: code = Unknown desc = Source image rejected: None of the signatures were accepted, reasons: Invalid GPG signature: gpgme.Signature{Summary:128, Fingerprint:"199E2F91FD431D51", Status:gpgme.Error{err:0x9}, Timestamp:time.Date(2022, time.June, 9, 15, 41, 29, 0, time.Local), ExpTimestamp:time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local), WrongKeyUsage:false, PKATrust:0x0, ChainModel:false, Validity:0, ValidityReason:error(nil), PubkeyAlgo:1, HashAlgo:8}; Invalid GPG signature: gpgme.Signature{Summary:128, Fingerprint:"199E2F91FD431D51", Status:gpgme.Error{err:0x9}, Timestamp:time.Date(2022, time.June, 9, 15, 41, 30, 0, time.Local), ExpTimestamp:time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local), WrongKeyUsage:false, PKATrust:0x0, ChainModel:false, Validity:0, ValidityReason:error(nil), PubkeyAlgo:1, HashAlgo:8}; Invalid GPG signature: gpgme.Signature{Summary:128, Fingerprint:"199E2F91FD431D51", Status:gpgme.Error{err:0x9}, Timestamp:time.Date(2022, time.June, 9, 15, 41, 30, 0, time.Local), ExpTimestamp:time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local), WrongKeyUsage:false, PKATrust:0x0, ChainModel:false, Validity:0, ValidityReason:error(nil), PubkeyAlgo:1, HashAlgo:8}; Invalid GPG signature: gpgme.Signature{Summary:128, Fingerprint:"199E2F91FD431D51", Status:gpgme.Error{err:0x9}, Timestamp:time.Date(2022, time.June, 9, 15, 41, 30, 0, time.Local), ExpTimestamp:time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local), WrongKeyUsage:false, PKATrust:0x0, ChainModel:false, Validity:0, ValidityReason:error(nil), PubkeyAlgo:1, HashAlgo:8}; Invalid GPG signature: gpgme.Signature{Summary:128, Fingerprint:"199E2F91FD431D51", Status:gpgme.Error{err:0x9}, Timestamp:time.Date(2022, time.June, 9, 15, 45, 11, 0, time.Local), ExpTimestamp:time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local), WrongKeyUsage:false, PKATrust:0x0, ChainModel:false, Validity:0, ValidityReason:error(nil), PubkeyAlgo:1, HashAlgo:8}; Invalid GPG signature: gpgme.Signature{Summary:128, Fingerprint:"199E2F91FD431D51", Status:gpgme.Error{err:0x9}, Timestamp:time.Date(2022, time.June, 9, 15, 45, 11, 0, time.Local), ExpTimestamp:time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local), WrongKeyUsage:false, PKATrust:0x0, ChainModel:false, Validity:0, ValidityReason:error(nil), PubkeyAlgo:1, HashAlgo:8}; Invalid GPG signature: gpgme.Signature{Summary:128, Fingerprint:"199E2F91FD431D51", Status:gpgme.Error{err:0x9}, Timestamp:time.Date(2022, time.June, 9, 15, 45, 11, 0, time.Local), ExpTimestamp:time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local), WrongKeyUsage:false, PKATrust:0x0, ChainModel:false, Validity:0, ValidityReason:error(nil), PubkeyAlgo:1, HashAlgo:8}; Invalid GPG signature: gpgme.Signature{Summary:128, Fingerprint:"199E2F91FD431D51", Status:gpgme.Error{err:0x9}, Timestamp:time.Date(2022, time.June, 9, 15, 45, 12, 0, time.Local), ExpTimestamp:time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local), WrongKeyUsage:false, PKATrust:0x0, ChainModel:false, Validity:0, ValidityReason:error(nil), PubkeyAlgo:1, HashAlgo:8}
  Warning  Failed          23s (x4 over 2m6s)   kubelet            Error: ErrImagePull
  Normal   BackOff         11s (x6 over 2m5s)   kubelet            Back-off pulling image "registry.redhat.io/openshift4/ose-hello-openshift-rhel8@sha256:ee88016114c97dae2849195f1b0e94f65bd6cfe823ce569b9b0c6097296cdcf0"
  Warning  Failed          11s (x6 over 2m5s)   kubelet            Error: ImagePullBackOff
```

ちょっと長いですが、エラーの原因として`rpc error: code = Unknown desc = Source image rejected: None of the signatures were accepted, reasons: Invalid GPG signature`というログが確認できます。
要は署名が違うということですね。これは公開鍵が誤っているため署名の検証ができないことが原因です。
きちんと検証機能が働いていることが確認できました。

## 取得する署名ストアを間違えてみる
次は取得する署名ストアを別の宛先に変えてみましょう。
`51-worker-rh-registry-trust.bu`を以下のように修正していきます。
```51-worker-rh-registry-trust.bu
variant: openshift
version: 4.10.0
metadata:
  name: 51-worker-rh-registry-trust
  labels:
    machineconfiguration.openshift.io/role: worker
storage:
  files:
  - path: /etc/containers/policy.json
    mode: 0644
    overwrite: true
    contents:
      inline: |
        {
          "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"
                }
              ]
            },
            "docker-daemon": {
              "": [
                {
                  "type": "insecureAcceptAnything"
                }
              ]
            }
          }
        }
  - path: /etc/containers/registries.d/registry.redhat.io.yaml
    mode: 0644
    overwrite: true
    contents:
      inline: |
        docker:
            registry.redhat.io:
                sigstore: https://bummy.com/sigstore
```
公開鍵のpathを直した上で、`/etc/containers/registries.d/registry.redhat.io.yaml`の内容を書き換えます。ここでは`registry.redhat.io`のsigstoreの宛先を`https://bummy.com/sigstore`にしているため、別の署名ストアを参照することになっています。

MachineConfigを作成してapplyします。
```
$ butane 51-worker-rh-registry-trust.bu -o 51-worker-rh-registry-trust-test.yaml
$ oc apply -f 51-worker-rh-registry-trust-test.yaml
```

しばらく待って、ファイルの確認をします。
```
$ oc debug node/<ノード名> -- chroot /host cat /etc/containers/registries.d/registry.redhat.io.yaml
docker:
    registry.redhat.io:
        sigstore: https://bummy.com/sigstore
```

ではデプロイを試します。
```
$ oc new-project test-project2
$ oc apply -f pod.yaml
```

状態を確認すると、先ほどと同じく`ErrImagePull`になっています。Podのイベントを確認してみましょう。
```
...omit...

Events:
  Type     Reason          Age                   From               Message
  ----     ------          ----                  ----               -------
  Normal   Scheduled       13s   default-scheduler  Successfully assigned test-proejct2/hello-openshift to ip-10-0-184-16.ap-northeast-1.compute.internal
  Normal   AddedInterface  12s   multus             Add eth0 [10.131.0.9/23] from openshift-sdn
  Normal   Pulling         12s   kubelet            Pulling image "registry.redhat.io/openshift4/ose-hello-openshift-rhel8:v4.7"
  Warning  Failed          10s   kubelet            Failed to pull image "registry.redhat.io/openshift4/ose-hello-openshift-rhel8:v4.7": rpc error: code = Unknown desc = Source image rejected: Get "https://bummy.com/sigstore/openshift4/ose-hello-openshift-rhel8@sha256=ee88016114c97dae2849195f1b0e94f65bd6cfe823ce569b9b0c6097296cdcf0/signature-1": EOF
  Warning  Failed          10s   kubelet            Error: ErrImagePull
  Normal   BackOff         9s    kubelet            Back-off pulling image "registry.redhat.io/openshift4/ose-hello-openshift-rhel8:v4.7"
  Warning  Failed          9s    kubelet            Error: ImagePullBackOff
```

今度は`rpc error: code = Unknown desc = Source image rejected: Get "https://bummy.com/sigstore/openshift4/ose-hello-openshift-rhel8@sha256.../signature-1": EOF`というエラーが出ています。署名情報が取得できなかったことがわかります。



このように、OpenShiftの内部レジストリはイメージ署名を検証し、もし検証に失敗した場合はイメージが取得できないように設定することができます。


ちなみに、ここのエラーログをみてみると、取得しようとしている署名情報のPathが確認できます。
どうやら`/etc/registries.d/<レジストリ名>.yaml`で設定している`sigstore`情報を使って、以下のようなURLにアクセスして署名情報を取得する仕組みのようです。
```
https://<sigstoreのURL>/<イメージのPath>@sha256=<ダイジェスト>/signature-1
```

試しに以下のURLにアクセスすると、`registry.redhat.io/openshift4/ose-hello-openshift-rhel8:v4.7`の署名情報を取得することができます。
※ダイジェスト情報は[こちら](https://catalog.redhat.com/software/containers/openshift4/ose-hello-openshift-rhel8/5f7f1f8dd8bb1c6806f2f0ba?container-tabs=gti)の`Manifest List Digest`で確認できます。

```
https://registry.redhat.io/containers/sigstore/openshift4/ose-hello-openshift-rhel8@sha256=ee88016114c97dae2849195f1b0e94f65bd6cfe823ce569b9b0c6097296cdcf0/signature-1
```


# おわりに
今回はRed HatのレジストリからイメージをPullする際の検証機能を確認してみました。
しかし、ユーザーが本当に実現したいことは**開発チームが署名したイメージを、商用デプロイ前に検証する**といった使い方かと思います。

次回は`これはRed Hatが配布するイメージにしか検証してないけど、自分のレジストリで実行するにはどうすればいいの？`という疑問に答えるために、**別のクラスターの内部レジストリにある署名されたイメージをpullし、検証機能で確認する**といったシナリオの実現方法をまとめようと思います。


# 参考
* [公式ドキュメント：コンテナーイメージの署名](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.10/html/security_and_compliance/security-container-signature)
* [Container Image Signatures in OpenShift 4](https://luis-javier-arizmendi-alonso.medium.com/container-image-signatures-in-openshift-4-a62b9e1c1b5a)
* [コンテナのイメージ署名に関する現状](https://qiita.com/Yuhkih/items/fbad089046bfcf8d824a)
* [Verifying Red Hat container image signatures in OpenShift Container Platform 4](https://access.redhat.com/verify-images-ocp4)
* [policy.jsonの説明(GitHub)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)
* [registries.dの説明(GitHub)](https://github.com/containers/image/blob/main/docs/containers-registries.d.5.md)