社内レジストリミラーから元の名前のまま取得する
このラボは本物のk3s上で動きます
VMの中に、k3sが1台(v1.33.3+k3s1)起動していて、skopeo・podmanと、Ubuntuのdocker-registryパッケージ(CNCF distributionレジストリ)がインストールされています。最初の起動に、数分かかります。VMは、最初はインターネット(80/443)に届きます。ステップ3までが接続された状態での持ち込み準備で、ステップ4で、皆さんが外部への送信をふさいで、エアギャップ環境に変えます。採点は、外部からVMの8899ポートに入ってくるエージェントが行うので、すでに確立した接続とループバックをふさぐと、採点が切れます。
目標
プライベートCAで社内レジストリをHTTPSで立てて、イメージをダイジェストのまま移したあと、外部がふさがれた状態で、イメージ名を変えずに、podman・containerd・Kubernetesが、社内ミラーから取得するようにします。
なぜ重要なのか
エアギャップ環境で、マニフェストのイメージ名を、社内レジストリのアドレスにすべて書き換えると、チャートやマニフェストが外部と分かれてしまい、次のバージョンを取得するたびに、直し直す必要があります。ミラーは、名前はそのままにして、「そのレジストリは、ここから取得する」ということだけを、ランタイムに知らせます。ランタイムごとに設定ファイルが違うことが、落とし穴です。podmanはregistries.conf、containerdはhosts.toml、k3sはregistries.yamlです。そして、すべてに、プライベートCAを別に教える必要があります。
所要時間は60分ほどです。セッションが終わるとVMが消えるので、残したいファイルは、終了前に別に保管してください。
ステップ
/root/registry/pkiに、ルート(ca.crt・ca.key、CNAirgap Internal Root CA)と、registry.airgap.internalの証明書(registry.crt・registry.key、SAN)を作成して、/etc/hostsに127.0.0.1 registry.airgap.internalを追加してください。- 設定ファイル(
/etc/docker/registry/config.yml)で、社内レジストリが127.0.0.1:5000で、その証明書でHTTPSを公開するようにして、サービスを起動し、起動時にも立ち上がるようにしてください。レジストリのサービスアカウントが、キーを読める必要があります。 - 接続されている間に、
registry.k8s.io/e2e-test-images/busybox:1.36.1-1とregistry.k8s.io/pause:3.10を、社内レジストリの同じパス(registry.airgap.internal:5000/e2e-test-images/busybox:1.36.1-1、.../pause:3.10)に、ダイジェストを保存して移し、원래이름 다이제스트を(プレースホルダーは元の名前とダイジェストです)1行ずつ書いてください(記録先:/root/registry/digests.txt)。 - 再実行しても、ルールが1セットだけ残るスクリプトで、外部への送信をふさいでください(スクリプト:
/root/registry/egress.sh)。チェーンAIRGAP-EGRESSを、OUTPUTとFORWARDで1回ずつ使い、ループバック・確立済みの接続・10.42.0.0/16・10.43.0.0/16は通します。 - podman:
registry.k8s.ioのミラーを社内レジストリにして、プライベートCAを教えたあと(設定ファイル:/etc/containers/registries.conf.d/50-airgap-mirror.conf)、元の名前registry.k8s.io/e2e-test-images/busybox:1.36.1-1でpullしてください。 - containerd: hosts.tomlを手で書いて(ファイル:
/root/registry/certs.d/registry.k8s.io/hosts.toml)、k3s ctr -n airgap-ctr images pull --hosts-dir /root/registry/certs.d registry.k8s.io/pause:3.10で取得してください。 - Kubernetes: k3sが社内ミラーを使うようにして(設定ファイル:
/etc/rancher/k3s/registries.yaml)、k3sを再起動したあと、ネームスペースairgapに、元の名前でimagePullPolicy: AlwaysのPodmirror-busybox(マニフェスト:/root/registry/mirror-busybox.yaml)を、Runningにしてください。 - 持ち込み記録を残してください(ファイル:
/root/registry/mirror-record.json)。images配列の項目ごとに、ref・mirror_ref・digest・pulled_by(podman・ctr・kubeletのうち、実際に取得したもの)を、最上位にegress_blockedを書きます。
参考
- 証明書: プライベートCAのモジュールと、同じ手順です(SANは必須)。
- レジストリの応答:
curl --cacert /root/registry/pki/ca.crt https://registry.airgap.internal:5000/v2/_catalog - ダイジェスト:
skopeo inspect --format '{{.Digest}}' docker://<이미지>(プレースホルダーはイメージです)・保存して移す:skopeo copy --all --preserve-digests ... - podmanとskopeoの、レジストリのCAの置き場所:
/etc/containers/certs.d/<호스트:포트>/ca.crt(プレースホルダーはホストとポートです) - k3sが作成したcontainerdの設定:
/var/lib/rancher/k3s/agent/etc/containerd/ - よくある間違い1: レジストリのキーを、rootだけが読めるようにしておくこと。サービスは専用アカウントで動くので、キーが読めずに落ちます。
journalctl -u docker-registryを見てください。 - よくある間違い2:
--allなしで移すこと。複数のアーキテクチャの一覧が、1つのアーキテクチャに減ってダイジェストが変わり、ダイジェストで固定したマニフェストが、ミラーでイメージを見つけられなくなります。
社内レジストリの証明書
/root/registry/pkiに、ルート(ca.crt・ca.key)と、registry.airgap.internalの証明書(registry.crt・registry.key、SAN)を作成して、/etc/hostsに名前を追加してください。
ルートは、自己署名でCA:TRUE、サーバー証明書は、ルートで署名しながら、拡張ファイルでSANを指定します。プライベートCAのモジュールと同じ手順です。
社内レジストリをHTTPSにする
設定ファイル(/etc/docker/registry/config.yml)で、レジストリが127.0.0.1:5000で、その証明書でHTTPSを公開するようにして、サービスを起動し、起動時にも立ち上がるようにしてください。
設定のhttp節に、アドレスとtls(証明書とキーのパス)を指定します。サービスは専用アカウントで動くので、キーを、そのアカウントが読める場所・権限に置く必要があります。起動しなければ、journalctl -u docker-registryから見てください。
ダイジェストを保存して移す
2つのイメージを、社内レジストリの同じパスに、ダイジェストを保存して移し、원래이름 다이제스트を(プレースホルダーは元の名前とダイジェストです)1行ずつ書いてください(記録先: /root/registry/digests.txt)。
skopeo copyは、レジストリからレジストリへ直接移します。複数のアーキテクチャの一覧を丸ごと移して初めて、ダイジェストが外部と同じになります。社内レジストリのCAは、/etc/containers/certs.d/<ホスト:ポート>/ca.crtに置けば、skopeoとpodmanが一緒に使います。
外部への送信をふさいで、エアギャップ環境にする
再実行しても、ルールが1セットだけ残るスクリプトで、外部への送信をふさいでください(スクリプト: /root/registry/egress.sh)。
新しいチェーンを作って、OUTPUTとFORWARDの先頭からそこへ送れば、空にして埋め直す方式で、何度実行しても同じ形になります。採点エージェントと、Kubernetesのアドレス範囲を、先に通してください。
podman: registries.confのmirror
registry.k8s.ioのミラーを社内レジストリにして(設定ファイル: /etc/containers/registries.conf.d/50-airgap-mirror.conf)、元の名前でbusyboxをpullしてください。
registries.confの[[registry]]は、prefixでどの名前に適用するかを、[[registry.mirror]]は、先に試す場所を書きます。名前は変えません。プライベートCAは、ステップ3で置いたcerts.dが、そのまま使われます。
containerd: hosts.tomlを手で書く
hosts.tomlを書いて(ファイル: /root/registry/certs.d/registry.k8s.io/hosts.toml)、k3s ctr -n airgap-ctr images pull --hosts-dir /root/registry/certs.d registry.k8s.io/pause:3.10で取得してください。
ディレクトリ名が元のレジストリで、ファイルの中のserverが元のアドレス、[host."..."]が先に試すミラーです。ミラーには、pull・resolveの権限と、CAのパスを書きます。
Kubernetes: registries.yamlとAlwaysのPod
k3sが社内ミラーを使うようにして(設定ファイル: /etc/rancher/k3s/registries.yaml)、再起動したあと、imagePullPolicy: AlwaysのPod mirror-busybox(マニフェスト: /root/registry/mirror-busybox.yaml)を、ネームスペースairgapでRunningにしてください。
mirrorsに元のレジストリとendpointを、configsに、ミラーのアドレスのtls ca_fileを書きます。k3sのドキュメントのとおり、変更したあとは、k3sを再起動する必要があります。イメージ名は、元のままにします。Alwaysでも、ミラーからタグを解決できれば、起動します。
持ち込み記録を、レジストリ・ランタイムと照合する
持ち込み記録を残してください(ファイル: /root/registry/mirror-record.json)。イメージごとに、ref・mirror_ref・digest・pulled_byと、最上位のegress_blockedを書きます。
ダイジェストは、社内レジストリに問い合わせて取り出し、pulled_byには、podman・ctr・kubeletのうち、実際にそのイメージを取得したものを書きます。値は、手で写さず、コマンドの出力から集めてください。