RHEL8にdocker registryを構築し、CP4Dのプライベート・コンテナー・レジストリとして使う


docker registryをデプロイするためには、dockerをインストールする必要があります。しかし、[こちら](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/building_running_and_managing_containers/con_running-containers-without-docker_assembly_starting-with-containers)のとおり、RHEL8ではDockerが廃止されています。

Dockerが廃止されたRHEL8でも、podman-docker パッケージをインストールすることにより、docker registryを正常に稼働させることができました。さらに、それをCP4D のプライベート・コンテナー・レジストリーとして使用できることも確認しました。

## Registry サーバーのデプロイ
検証環境(作業開始時点)

```
# cat /etc/redhat-release
Red Hat Enterprise Linux release 8.9 (Ootpa)
# podman --version
podman version 4.6.1

# rpm -aq | grep docker
# rpm -aq | grep podman
podman-plugins-4.6.1-8.module+el8.9.0+21525+acb5d821.x86_64
cockpit-podman-75-1.module+el8.9.0+21525+acb5d821.noarch
podman-gvproxy-4.6.1-8.module+el8.9.0+21525+acb5d821.x86_64
podman-4.6.1-8.module+el8.9.0+21525+acb5d821.x86_64
podman-catatonit-4.6.1-8.module+el8.9.0+21525+acb5d821.x86_64
```

podman-docker パッケージをインストールします。
```
# yum install podman-docker
サブスクリプション管理リポジトリーを更新しています。
Red Hat Enterprise Linux 8 for x86_64 - BaseOS (RPMs)                                                           7.0 kB/s | 4.1 kB     00:00
Red Hat Enterprise Linux 8 for x86_64 - Supplementary (RPMs)                                                    6.1 kB/s | 3.8 kB     00:00
Red Hat Enterprise Linux 8 for x86_64 - AppStream (RPMs)                                                        7.7 kB/s | 4.5 kB     00:00
依存関係が解決しました。
================================================================================================================================================
 パッケージ              アーキテクチャー バージョン                                           リポジトリー                               サイズ
================================================================================================================================================
インストール:
 podman-docker           noarch           3:4.6.1-8.module+el8.9.0+21525+acb5d821              rhel-8-for-x86_64-appstream-rpms           110 k

トランザクションの概要
================================================================================================================================================
インストール  1 パッケージ

ダウンロードサイズの合計: 110 k
インストール後のサイズ: 10 k
これでよろしいですか? [y/N]: y
パッケージのダウンロード:
podman-docker-4.6.1-8.module+el8.9.0+21525+acb5d821.noarch.rpm                                                  100 kB/s | 110 kB     00:01
------------------------------------------------------------------------------------------------------------------------------------------------
合計                                                                                                            100 kB/s | 110 kB     00:01
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
  準備             :                                                                                                                        1/1
  インストール中   : podman-docker-3:4.6.1-8.module+el8.9.0+21525+acb5d821.noarch                                                           1/1
  scriptletの実行中: podman-docker-3:4.6.1-8.module+el8.9.0+21525+acb5d821.noarch                                                           1/1
  検証             : podman-docker-3:4.6.1-8.module+el8.9.0+21525+acb5d821.noarch                                                           1/1
インストール済みの製品が更新されています。

インストール済み:
  podman-docker-3:4.6.1-8.module+el8.9.0+21525+acb5d821.noarch

完了しました!
```
これだけで docker registry を起動できるようになります。
```
# docker run -d -p 5000:5000 --restart=always --name registry registry:2
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
Resolved "registry" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/registry:2...
Getting image source signatures
Copying blob 68c26f40ad80 done
Copying blob 8f2a82336004 done
Copying blob 5daf2fb85fb9 done
Copying blob ca5f23059090 done
Copying blob 619be1103602 done
Copying config 9363667f8a done
Writing manifest to image destination
97f3aafdaadfd0d59f638e64bf592a5bf5da11b863c0b59a4cf7b0fc8720b997

# docker ps
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
CONTAINER ID  IMAGE                         COMMAND               CREATED        STATUS        PORTS                   NAMES
97f3aafdaadf  docker.io/library/registry:2  /etc/docker/regis...  5 seconds ago  Up 5 seconds  0.0.0.0:5000->5000/tcp  registry

```

## ストレージ保存場所の変更

ストレージ保存場所を変更したい場合の registry 起動コマンド例です。以下の場合、`/registry`を保存場所として指定しています。
```
# docker container rm -v registry
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
registry

# docker run -d \
> -p 5000:5000 \
> --restart=always \
> --name registry \
> -v /registry:/var/lib/registry \
> registry:2
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
4d9c3fa40289125dcfd4fde147ae2d9d0d71fa171f7f93626796c47e3f485c8a

# docker ps
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
CONTAINER ID  IMAGE                         COMMAND               CREATED         STATUS         PORTS                   NAMES
4d9c3fa40289  docker.io/library/registry:2  /etc/docker/regis...  10 seconds ago  Up 10 seconds  0.0.0.0:5000->5000/tcp  registry
```

## 外部からアクセス可能な registry とする
外部アクセス可能な registry にするためには、TLSを有効化する必要があります。以下、自己署名証明書使用の手順例です。


1. 自己署名証明書を生成します。

    必ず `myregistry.domain.com` という名前を CN として使用します。

    ```
    # mkdir -p certs
    # openssl req \
    >   -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key \
    >   -addext "subjectAltName = DNS:myregistry.domain.com" \
    >   -x509 -days 365 -out certs/domain.crt
    Generating a RSA private key
    ..................................................................................................................................................++++
    ........................................................................................................................................................................................++++
    writing new private key to 'certs/domain.key'
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [XX]:JP
    State or Province Name (full name) []:Tokyo
    Locality Name (eg, city) [Default City]:Tokyo
    Organization Name (eg, company) [Default Company Ltd]:example
    Organizational Unit Name (eg, section) []:test
    Common Name (eg, your name or your server's hostname) []:myregistry.domain.com
    Email Address []:xxxxx@yyyyy
    
    # ls -l certs
    合計 8
    -rw-r--r--. 1 root root 2179  4月 18 14:19 domain.crt
    -rw-------. 1 root root 3272  4月 18 14:15 domain.key
    ```
   
2. 1の結果を使用して、TLS を有効化した registry を起動します。ポート番号は、デフォルトの HTTPS ポートである 443 で実行します。
    
    ```
    # docker container stop registry
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    registry
    
    #  docker container rm -v registry
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    registry
    # docker run -d \
    >     --restart=always \
    >     --name registry \
    >     -v "$(pwd)"/certs:/certs \
    >     -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
    >     -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
    >     -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
    >     -p 443:443 \
    >     -v /registry:/var/lib/registry \
    >     registry:2
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    569836ccf760a1a23db321de1908263a275c7adc2c0eee9ea884b30b08339f10
    
    # docker ps
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    CONTAINER ID  IMAGE                         COMMAND               CREATED         STATUS         PORTS                 NAMES
    569836ccf760  docker.io/library/registry:2  /etc/docker/regis...  38 seconds ago  Up 39 seconds  0.0.0.0:443->443/tcp  registry
     ```

3. `myregistry.domain.com` の名前の解決を可能にするため、当該環境のDNSサーバー または /etc/hosts に `myregistry.domain.com` のエントリーを追加します。
4. すべての Docker デーモンにその証明書を信頼するように指示します。 これを行う方法は、OS によって異なります。
この設定をしないと、アクセス時次のようなエラーになります。
    ```
    Error: authenticating creds for "myregistry.domain.com:443": pinging container registry myregistry.domain.com:443: Get "https://myregistry.domain.com:443/v2/": tls: failed to verify certificate: x509: certificate signed by unknown authority
    ```
   設定手順は、ページ下部[自己署名証明書を信頼させるための設定 (接続クライアント側)](#自己署名証明書を信頼させるための設定 (接続クライアント側)) をご参照ください。


## ネイティブベーシック認証
アクセス制御のため、ベーシック認証を有効化します。
シークレットの保管にhtpasswd を使用したネイティブベーシック認証を使用する手順例です。なお、認証を機能させるには、先にTLSを構成しておく必要があります。

1. htpasswdファイルを作成します。user: `testuser`、password: `testpassword`のコマンド例です。
    
   ```
    # mkdir auth
    # docker run \
      --entrypoint htpasswd \
      httpd:2 -Bbn testuser testpassword > auth/htpasswd
   ```
    
2. ベーシック認証付きでレジストリを再起動します。
    ```
    # docker container rm -v registry
    
    # docker run -d \
      --restart=always \
      --name registry \
      -v "$(pwd)"/auth:/auth \
      -e "REGISTRY_AUTH=htpasswd" \
      -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
      -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
      -v "$(pwd)"/certs:/certs \
      -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
      -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
      -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
      -p 443:443 \
      -v /registry:/var/lib/registry \
      registry:2
    ```

3. 設定したuser/passwordでログインできることを確認します。
    ```
    # podman login myregistry.domain.com:443
    Username: testuser
    Password:
    Login Succeeded!
    ```


## 自己署名証明書を信頼させるための設定 (接続クライアント側)

この registry を使用する(ログイン、イメージのプル等を実行する)すべてのシステムにて、当該自己署名証明書を信頼するよう次の手順を実施します。CP4D のプライベート・コンテナー・レジストリーとして、この registry を使用する場合、すべての workerノードでこちらの設定が必要です。

**RHEL8**および**RHCOS4.14**では、次の２つの方法で成功することを確認済みです。

`certs/domain.crt` ファイルを次のいずれかとしてコピーします。

- /etc/containers/certs.d/myregistry.domain.com:443/ca.crt
    - `/etc/containers/certs.d` 下のディレクトリ名は `registry名:ポート番号` に一致している必要があります。
    - ファイル名は`<任意の文字列>.crt`（例： ca.crt）である必要があります。
- /etc/pki/ca-trust/source/anchors/myregistrydomain.com.crt
    - システム全体でのトラストストアを使用する方法です。
    - こちらにファイルをコピーすると、当registryが`trust list` に即時に追加されますが、システム全体のトラストストア設定を更新するには `update-ca-trust` コマンドを実行する必要があります。なお、ファイル名に特に規定はないようです。

   ```text: workerノードでの実行例
     $ oc debug node/worker-0
    Temporary namespace openshift-debug-mmzcb is created for debugging node...
    Starting pod/worker-0-debug ...
    To use host binaries, run `chroot /host`
    Pod IP: 192.168.10.104
    If you don't see a command prompt, try pressing enter.
    sh-4.4# chroot /host
    sh-5.1# trust list | grep my
    sh-5.1# sudo scp root@myregistry.domain.com:/root/certs/domain.crt /etc/pki/ca-trust/source/anchors/myregistrydomain.com.crt
    root@myregistry.domain.com's password:
    domain.crt                                                                                                                                                                                        100% 2179     3.1MB/s   00:00
    sh-5.1# trust list | grep my
    label: myregistry.domain.com
    sh-5.1# mkdir /etc/containers/certs.d/myregistry.domain.com:443
    sh-5.1# cp /etc/pki/ca-trust/source/anchors/myregistrydomain.com.crt /etc/containers/certs.d/myregistry.domain.com:443/ca.crt
    sh-5.1# ls -l /etc/containers/certs.d/myregistry.domain.com:443/ca.crt
    -rw-r--r--. 1 root root 2179 May  2 03:59 /etc/containers/certs.d/myregistry.domain.com:443/ca.crt
    ```

### 補足
#### 1. curlでのアクセスの場合
curlコマンドを使用してAPI実行する場合は、上の設定は効きません。下のコマンド例のように、`--cacert` オプションで証明書ファイルを指定する必要があります。
```
curl -u testuser:testpassword https://myregistry.domain.com/v2/_catalog --cacert /etc/containers/certs.d/myregistry.domain.com:443/ca.crt
```
#### 2. registryのログ確認方法
registryへのアクセスでエラーが出る場合など、registryのログを確認すると解決のヒントが見つかる場合があります。
コマンド例：
```
docker logs -f registry
```

### 参考

https://matsuand.github.io/docs.docker.jp.onthefly/registry/deploying/

https://matsuand.github.io/docs.docker.jp.onthefly/registry/insecure/#use-self-signed-certificates

https://access.redhat.com/solutions/7035834

https://access.redhat.com/documentation/ja-jp/red_hat_quay/3.5/html/manage_red_hat_quay/_configuring_podman_to_trust_the_certificate_authority

https://docs.docker.jp/engine/security/certificates.html


https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/system_design_guide/using-shared-system-certificates_system-design-guide#the-system-wide-trust-store_using-shared-system-certificates