fbpx

Mirantis Secure Registry 3 (MSR)をk0sで動かそう #k0s #msr #k8s #kubernetes #mirantis

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

Mirantis Secure Registryとは、DockerコンテナイメージやHelm Chartを安全に保管・共有・管理できるプライベートレジストリです。2021年末にリリースされたバージョン3.0.0にて、従来より統合されていたMirantis Kubernetes Engineから独立し、標準的なKubernetes上で動作可能となりました。
本稿ではMirantis Secure Registry (以下MSR)バージョン3.0.0を、ワンバイナリで動作する軽量なフル機能Kubernetesディストリビューションであるk0sにデプロイしてみます。
なお、本稿ではMSRの詳細な設定手順や利用方法は省略します。公式ドキュメントを参照いただくか、今後予定されているMirantis社公認トレーニングの開催をお待ちください。

Vagrantfile

VirtualboxとVagrantで仮想マシンを1台作成し、そちらにk0sとMSRをインストールします。

base_install =<<-SHELL
timedatectl set-timezone Asia/Tokyo
yum install -y ntp
systemctl enable ntpd
systemctl start ntpd
SHELL
Vagrant.configure("2") do |config|
config.vm.box = "centos/7"
config.vm.box_check_update = false
config.vm.define "node1" do |cf|
cf.vm.hostname = "node1"
cf.vm.network "private_network", ip: "192.168.56.101"
cf.vm.provision "shell", inline: base_install
cf.vm.provider "virtualbox" do |vb|
vb.memory = 12288
vb.cpus = 4
end
end
end

k0s自体は軽量ですが、MSRのセキュリティスキャンを利用する場合はパワフルな環境が必要であるためメモリ12GB・4vCPUと設定しています。当初はメモリ4GB・1vCPUでしたが正常に動作せず、次にメモリ8GB・2vCPUと倍にしても動作に支障が見られたためです。詳細はSystem requirementsを参照してください。

k0sインストール

Virtualbox/Vagrantでのk0sインストールについての詳細は過去記事「k0sでKubernetesをVirtualbox/Vagrantにインストールしてみよう」をご覧ください。以降では手順をかいつまんで記述します。

k0sは本稿執筆時点で最新の v1.23.1+k0s.0 を利用します。

[vagrant@node1 ~]$ /usr/local/bin/k0s version
v1.23.1+k0s.0

デフォルトのインストール設定ファイルにて、IPアドレスを変更します。

[vagrant@node1 ~]$ k0s config create > k0s.yaml.orig
[vagrant@node1 ~]$ cp -a k0s.yaml.orig k0s.yaml
[vagrant@node1 ~]$ vi k0s.yaml
[vagrant@node1 ~]$ sudo mkdir /etc/k0s
[vagrant@node1 ~]$ sudo mv k0s.yaml /etc/k0s/
[vagrant@node1 ~]$ diff -u k0s.yaml.orig /etc/k0s/k0s.yaml
--- k0s.yaml.orig 2022-01-04 15:08:14.826736210 +0900
+++ /etc/k0s/k0s.yaml 2022-01-04 15:10:39.865766191 +0900
@@ -5,11 +5,10 @@
name: k0s
spec:
api:
- address: 10.0.2.15
+ address: 192.168.56.101
k0sApiPort: 9443
port: 6443
sans:
- - 10.0.2.15
- 192.168.56.101
- fe80::5054:ff:fe4d:77d3
- fe80::a00:27ff:fefc:8421
@@ -81,7 +80,7 @@
scheduler: {}
storage:
etcd:
- peerAddress: 10.0.2.15
+ peerAddress: 192.168.56.101
type: etcd
telemetry:
enabled: true

シングルノードでインストールします。k0sは、デフォルトではコントローラノードでワークロードを動作できないようになっていますが、 --single オプションを付与してシングルノードインストールを行うと、コントローラノードがワーカーノードを兼用し、1ノードでKubernetesを起動できるようになります。

[vagrant@node1 ~]$ sudo /usr/local/bin/k0s install controller -c /etc/k0s/k0s.yaml --single --kubelet-extra-args "--node-ip=192.168.56.101"
[vagrant@node1 ~]$ sudo /usr/local/bin/k0s start
[vagrant@node1 ~]$ sudo /usr/local/bin/k0s status
Version: v1.23.1+k0s.0
Process ID: 3862
Role: controller
Workloads: true
SingleNode: true

Roleが「controller」でありつつ、Workloadsも「true」となっていることがわかります。kubectl get nodesでも見てみましょう。

[vagrant@node1 ~]$ mkdir .kube
[vagrant@node1 ~]$ sudo cp /var/lib/k0s/pki/admin.conf .kube/config
[vagrant@node1 ~]$ sudo chown $USER:$USER .kube/config
[vagrant@node1 ~]$ chmod 600 .kube/config
[vagrant@node1 ~]$ export KUBECONFIG=~/.kube/config
[vagrant@node1 ~]$ k0s kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready control-plane 82s v1.23.1+k0s

デフォルトのk0sインストールなら「No resources found」となってしまいますが、シングルノードインストールであればコントローラノードもワークロードを動作可能であるため、このような結果となります。

以上でk0sによるKubernetesのインストールは完了です。

Helmインストール

MSRバージョン3と関連ソフトウェアはHelmでインストールするようになっているので、まずHelmをインストールしましょう。本稿執筆時点で最新のv3.7.2を使用します。

[vagrant@node1 ~]$ curl -LO https://get.helm.sh/helm-v3.7.2-linux-amd64.tar.gz -LO https://get.helm.sh/helm-v3.7.2-linux-amd64.tar.gz.sha256sum
[vagrant@node1 ~]$ sha256sum -c helm-v3.7.2-linux-amd64.tar.gz.sha256sum
helm-v3.7.2-linux-amd64.tar.gz: OK
[vagrant@node1 ~]$ tar xf helm-v3.7.2-linux-amd64.tar.gz
[vagrant@node1 ~]$ sudo mv linux-amd64/helm /usr/local/bin/
[vagrant@node1 ~]$ helm version
version.BuildInfo{Version:"v3.7.2", GitCommit:"663a896f4a815053445eec4153677ddc24a0a361", GitTreeState:"clean", GoVersion:"go1.16.10"}

以上でHelmのインストールは完了です。

ボリュームの準備

MSRバージョン3では永続データの格納用に、3つのボリュームを使用します。PrerequisitesではPersistent Volume (PV)の動的プロビジョニングを要求していますが、本稿は1ノードであり内容を単純化するために、hostPathの静的PVを準備・利用することとします。

まず、脆弱性スキャンデータ用ボリュームのディレクトリを作成し、所有者・所属グループを 101:103 で設定しておきます。ボリューム用ディレクトリは割り当ての際に自動的に作成されるのですが、所有者・所属グループが root:root となり書き込みできずエラーとなるため、あらかじめ作成・設定しています。この他、イメージデータ用・レポジトリメタデータ用の2ディレクトリは自動作成・設定される root:root で問題ないため、ここでは作成しません。

[vagrant@node1 ~]$ sudo mkdir -p /vols/msr-scanningstore
[vagrant@node1 ~]$ sudo chown 101:103 /vols/msr-scanningstore
[vagrant@node1 ~]$ ls -ld /vols/msr-scanningstore
drwxr-xr-x. 2 101 103 6 Jan 4 15:58 /vols/msr-scanningstore

次のYAMLマニフェストでPVを作成します。

apiVersion: v1
kind: PersistentVolume
metadata:
name: msr-hostpath
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 20Gi
hostPath:
path: /vols/msr
apiVersion: v1
kind: PersistentVolume
metadata:
name: rethinkdb-cluster-hostpath
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
hostPath:
path: /vols/msr-rethinkdb-cluster
apiVersion: v1
kind: PersistentVolume
metadata:
name: scanningstore-hostpath
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 24Gi
hostPath:
path: /vols/msr-scanningstore

各PVのspec.accessModes、access.capacity.storageは、MSRのHelm Chart内で指定されている値を用いています。Recommended system requirementsにImage data storageは25〜100GB推奨とあるので msr-hostpathは20Giでは足りない可能性があります。必要であれば適宜変更してください。

では、PVを作成します。

[vagrant@node1 ~]$ k0s kubectl apply -f static-pv.yaml
persistentvolume/msr-hostpath created
persistentvolume/rethinkdb-cluster-hostpath created
persistentvolume/scanningstore-hostpath created
[vagrant@node1 ~]$ k0s kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
msr-hostpath 20Gi RWX Retain Available 21s
rethinkdb-cluster-hostpath 1Gi RWO Retain Available 21s
scanningstore-hostpath 24Gi RWO Retain Available 21s

作成できました。

関連ソフトウェアのインストール

Prerequisitesでは、関連ソフトウェアとしてcert-managerPostgres Operatorのインストールが求められているので、手順に従って実施します。

cert-manager

[vagrant@node1 ~]$ k0s kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io created
namespace/cert-manager created
serviceaccount/cert-manager-cainjector created
serviceaccount/cert-manager created
serviceaccount/cert-manager-webhook created
clusterrole.rbac.authorization.k8s.io/cert-manager-cainjector created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-issuers created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-clusterissuers created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-certificates created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-orders created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-challenges created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-ingress-shim created
clusterrole.rbac.authorization.k8s.io/cert-manager-view created
clusterrole.rbac.authorization.k8s.io/cert-manager-edit created
clusterrole.rbac.authorization.k8s.io/cert-manager-controller-approve:cert-manager-io created
clusterrole.rbac.authorization.k8s.io/cert-manager-webhook:subjectaccessreviews created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-cainjector created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-issuers created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-clusterissuers created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-certificates created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-orders created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-challenges created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-ingress-shim created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-controller-approve:cert-manager-io created
clusterrolebinding.rbac.authorization.k8s.io/cert-manager-webhook:subjectaccessreviews created
role.rbac.authorization.k8s.io/cert-manager-cainjector:leaderelection created
role.rbac.authorization.k8s.io/cert-manager:leaderelection created
role.rbac.authorization.k8s.io/cert-manager-webhook:dynamic-serving created
rolebinding.rbac.authorization.k8s.io/cert-manager-cainjector:leaderelection created
rolebinding.rbac.authorization.k8s.io/cert-manager:leaderelection created
rolebinding.rbac.authorization.k8s.io/cert-manager-webhook:dynamic-serving created
service/cert-manager created
service/cert-manager-webhook created
deployment.apps/cert-manager-cainjector created
deployment.apps/cert-manager created
deployment.apps/cert-manager-webhook created
mutatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
[vagrant@node1 ~]$ k0s kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.crds.yaml
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io configured
customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io configured

cert-managerネームスペースを確認し、各オブジェクトが問題ないことを確認します。

[vagrant@node1 ~]$ k0s kubectl -n cert-manager get all
NAME READY STATUS RESTARTS AGE
pod/cert-manager-748f4d667d-7s7xk 1/1 Running 0 79s
pod/cert-manager-cainjector-6cd5988f45-n7f5d 1/1 Running 0 79s
pod/cert-manager-webhook-7498ddfd98-l78p2 1/1 Running 0 79s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cert-manager ClusterIP 10.107.197.71 9402/TCP 79s
service/cert-manager-webhook ClusterIP 10.105.218.129 443/TCP 79s

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/cert-manager 1/1 1 1 79s
deployment.apps/cert-manager-cainjector 1/1 1 1 79s
deployment.apps/cert-manager-webhook 1/1 1 1 79s

NAME DESIRED CURRENT READY AGE
replicaset.apps/cert-manager-748f4d667d 1 1 1 79s
replicaset.apps/cert-manager-cainjector-6cd5988f45 1 1 1 79s
replicaset.apps/cert-manager-webhook-7498ddfd98 1 1 1 79s

Postgres Operator

[vagrant@node1 ~]$ helm repo add postgres-operator https://opensource.zalando.com/postgres-operator/charts/postgres-operator/
"postgres-operator" has been added to your repositories
[vagrant@node1 ~]$ helm repo up
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "postgres-operator" chart repository
Update Complete. Happy Helming!
[vagrant@node1 ~]$ helm install postgres-operator postgres-operator/postgres-operator \
--set configKubernetes.spilo_runasuser=101 \
--set configKubernetes.spilo_runasgroup=103 \
--set configKubernetes.spilo_fsgroup=103
manifest_sorter.go:192: info: skipping unknown hook: "crd-install"
manifest_sorter.go:192: info: skipping unknown hook: "crd-install"
manifest_sorter.go:192: info: skipping unknown hook: "crd-install"
NAME: postgres-operator
LAST DEPLOYED: Tue Jan 4 16:07:15 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
To verify that postgres-operator has started, run:

kubectl --namespace=default get pods -l "app.kubernetes.io/name=postgres-operator"

最後に表示されたコマンドを実行して、問題がないことを確認します。

[vagrant@node1 ~]$ k0s kubectl --namespace=default get pods -l "app.kubernetes.io/name=postgres-operator"
NAME READY STATUS RESTARTS AGE
postgres-operator-867bf8f978-tw4f9 1/1 Running 0 35s

MSRバージョン3のインストール

では、MSRバージョン3のインストールを行いましょう。

デフォルトではMSRのWebUIがClusterIPサービスで内部公開されるようになっているので、NodePortサービスで外部公開するように変更します。
また、ここではライセンスファイル「license.lic」を与えていますが、インストール後にライセンスファイルを適用することもできます。なお、以前のMSRと異なり、ライセンスファイルをWebUIからアップロードするのではなく、Helmコマンドで適用するようになっています。

[vagrant@node1 ~]$ helm install msr msr \
--repo https://registry.mirantis.com/charts/msr/msr \
--version 1.0.0 \
--set-file license=license.lic \
--set service.type=NodePort
NAME: msr
LAST DEPLOYED: Tue Jan 4 16:24:04 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Release Notes: http://docs.mirantis.com/msr-3.0-01

1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace default -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}' services msr)
export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
echo https://$NODE_IP:$NODE_PORT

2. Run MSR CLI commands as follows:
kubectl -n default exec -it deploy/msr-api -- msr <command></command>

3. Modifying rethinkdb.cluster.replicaCount requires additional steps to scale
the MSR and eNZi databases (table replicas):

AFTER increasing replicaCount, run the command:
kubectl -n default exec -it deploy/msr-api -- msr db scale

BEFORE decreasing replicaCount:
- Decommission RethinkDB servers from highest to lowest:
- Get a list of servers to decommission:
kubectl -n default exec -it deploy/msr-api -- msr rethinkdb list
- Decommission servers with:
kubectl -n default exec -it deploy/msr-api -- msr rethinkdb decommission
- Scale the database:
kubectl -n default exec -it deploy/msr-api -- msr db scale

もし「timed out waiting for the condition」エラーとなってしまったら、しばらく待ってから「helm status msr」を実行してください。同じような使用方法のドキュメントが表示されるはずです。されない場合はその他の問題が発生している可能性がありますので、Podのログなどを確認してみてください。

よくあるパターンとしては必要なPVがうまく準備できていない場合などです。例えば脆弱性スキャンデータ用ボリュームのディレクトリの所有者・所属グループが root:root となっていて書き込みができないと、msr-scanningstore-0 Podのログが次のようになっています。

[vagrant@node1 ~]$ k0s kubectl logs msr-scanningstore-0
mkdir: cannot create directory ‘/home/postgres/pgdata/pgroot’: Permission denied
mkdir: cannot create directory ‘/home/postgres/pgdata/pgroot’: Permission denied
touch: cannot touch '/home/postgres/pgdata/pgroot/pg_log/postgresql-0.csv': No such file or directory
(略)

MSRのWebUIへのアクセス

MSRのWebUIにアクセスするためのURLは、デプロイ成功時に表示されたコマンドで得ることができます。ただし、本稿では素のkubectlではなくk0s同梱のものを使っているため、kubectlの前にk0sを付与する必要があります。

[vagrant@node1 ~]$ export NODE_PORT=$(k0s kubectl get --namespace default -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}' services msr)
[vagrant@node1 ~]$ export NODE_IP=$(k0s kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
[vagrant@node1 ~]$ echo https://$NODE_IP:$NODE_PORT
https://192.168.56.101:30211

こちらにウェブブラウザでアクセスしてみましょう。

管理者のIDは「admin」、パスワードは「password」となっています。これらはMSRのHelm Chart内で指定されているので、興味のある方はHelm Chartを取得して調べてみてください。

脆弱性スキャンの有効化

管理者としてログインできたら、まずは脆弱性スキャンを有効にしてみましょう。左のサイドバーから System を開き、上の Security タブを開きます。Enable Scanning をトグルし、Sync Database now ボタンをクリックします。脆弱性スキャン用のデータベース初期化が始まります。メモリ12GB・4vCPUですが、30分以上かかりましたので気長に待ちましょう。

ユーザ、オーガニゼーション、チームの作成

MSRにはユーザ、オーガニゼーション、チームという概念があり、細かな権限設定やユーザのグルーピングを行えるようになっています。
ここではすべてをまとめるオーガニゼーションとして「creationline」があり、「dev」と「ops」の2チームが所属しており、「dev」チームには「alice」ユーザが、「ops」チームには「bob」ユーザが所属しているとします。
さらに「creationline」オーガニゼーションはプライベートなコンテナイメージレポジトリとして「logstash」を持っており、このレポジトリに対して「dev」チームは読み書き権限を持ち、「ops」チームは読み込み専用権限を持っているとします。
箇条書きでまとめると次のようになります。

  • オーガニゼーション: creationline
    • レポジトリ: logstash
    • チーム: dev
      • creationline/logstash レポジトリに R/W 権限を持つ
      • ユーザ alice が所属
    • チーム: ops
      • creationline/logstash レポジトリに R/O 権限を持つ
      • ユーザ bob が所属

MSRのレポジトリにDockerコンテナイメージをプッシュ・プル

MSRの creationline/logstash レポジトリにイメージをプッシュしてみましょう。MSRをインストールした仮想マシンではなく、ホスト側で実施していきます。

まず事前準備として、MSR (192.168.56.101:30211) をDockerデーモンに信頼させます。/etc/docker/daemon.json に insecure-registries キーを追加し、Dockerデーモンを再起動します。

{
"insecure-registries": [ "192.168.56.101:30211" ]
}
% sudo systemctl restart docker

これを行わないと、docker login実施時に「Error response from daemon: Get "https://192.168.56.101:30211/v2/": x509: certificate is valid for 127.0.0.1, ::1, not 192.168.56.101」というエラーとなってしまうので忘れずに実施しましょう。

ではaliceユーザでMSRにログインします。

% docker login 192.168.56.101:30211
Username: alice
Password:
Login Succeeded

ログインできました。
elastic/logstash:7.13.3 イメージをMSRにプッシュしてみましょう。

% docker image tag elastic/logstash:7.13.3 192.168.56.101:30211/creationline/logstash:7.13.3
% docker image push 192.168.56.101:30211/creationline/logstash:7.13.3
The push refers to repository [192.168.56.101:30211/creationline/logstash]
752595536bdb: Pushed
d83131943e63: Pushed
3865b0219f35: Pushed
2d7bcb60ab83: Pushed
8939b09a3291: Pushed
c49f4b89350d: Pushed
9a543ee7f3be: Pushed
25709602f08b: Pushed
627247ba7b32: Pushed
c646254d9f2f: Pushed
174f56854903: Pushed
7.13.3: digest: sha256:a24e6faa2337f4deaa903eb9036f01387414252d3f6649f0bfe471b897310c63 size: 2823

プッシュ成功しました。WebUIでも確認してみましょう。

このようにGUIで確認することが可能です。VulnerabilitiesがPendingとなっているのは脆弱性スキャン中という意味です。完了まで少々時間がかかるので、他のことをして待ちましょう。

aliceはcreationline/logstashに書き込み権限がありましたが、読み込み専用権限のbobではどうでしょうか。まずaliceはログアウトし、bobでログインし直します。

% docker logout 192.168.56.101:30211
Removing login credentials for 192.168.56.101:30211
% docker login 192.168.56.101:30211
Username: bob
Password:
Login Succeeded

次に elastic/logstash:7.16.2 をプッシュしてみます。

% docker image tag elastic/logstash:7.16.2 192.168.56.101:30211/creationline/logstash:7.16.2
% docker image push 192.168.56.101:30211/creationline/logstash:7.16.2
The push refers to repository [192.168.56.101:30211/creationline/logstash]
e4cc9a5205b5: Preparing
22cadb4a5d42: Preparing
046452805d26: Preparing
a9d36c36d24e: Preparing
4800551e926b: Preparing
0fc8b3a8b4d0: Waiting
15b6bb42d46c: Waiting
ae62b93394a2: Waiting
efb4c1e5fa42: Waiting
409c69e46596: Waiting
174f56854903: Waiting
denied: requested access to the resource is denied

想定通り、書き込み権限のないbobではプッシュができませんでした。一方、読み込み権限はあるのでプルは可能です。

% docker image rm 192.168.56.101:30211/creationline/logstash:7.13.3
Untagged: 192.168.56.101:30211/creationline/logstash:7.13.3
Untagged: 192.168.56.101:30211/creationline/logstash@sha256:a24e6faa2337f4deaa903eb9036f01387414252d3f6649f0bfe471b897310c63
% docker image rm elastic/logstash:7.13.3
Untagged: elastic/logstash:7.13.3
Untagged: elastic/logstash@sha256:9cc4f59ac3b393bd503d6d8da3700433868d42ffd3f311c348eb32df0c253f04
Deleted: sha256:289a765558773dc7dd973c3d04680c0d0ceddafc2584034ad02473460d7342dd
Deleted: sha256:45cec05d815e103ec7ba60b0eb474609e8473539be5923edf7e87b5bfe18a612
Deleted: sha256:3c713164fa71c59dafe691e6a46400bc73f661169c59ccfc5098cdd082188a9c
Deleted: sha256:5c34fc522aa05f888841ea414476fcd6720fc487c6b48b18abdc07b37356e4eb
Deleted: sha256:56da02333067501293ef10bbf8bc5372ff688517f3e033a4698202bddbbae281
Deleted: sha256:dd7f5008374c9cc9352db5a678281473e09e4d4353d7648b5621e1b3de43b80c
Deleted: sha256:6e788512c81251cc71dbb7c1bb5ccaa3dc68ed6dd54df53645ad41c3e431558e
Deleted: sha256:4b8fd99c44be485efecea68c665517b4c201b5dd994b4a73b060cc5bb8b409c7
Deleted: sha256:01771379ef4293584918fe3e7ac03cd9914f9340b9bf4ecc16e73f7bcb96a32b
Deleted: sha256:0e5d2b96638ea2d7a72eb2a4bbed989d4b51e669ec762789c990a81df92e7025
Deleted: sha256:ef3bce480b0f7489a66c12559f64f6155c7031261d58e6dda91f438aa7a79969
Deleted: sha256:5b0b92f3990e2772d58c559c866d08436febf471792ad8502fce1f6ef568449e
% docker image pull 192.168.56.101:30211/creationline/logstash:7.13.3
7.13.3: Pulling from creationline/logstash
2d473b07cdd5: Already exists
f653a67c9318: Pull complete
e9bd4c4e6834: Pull complete
f7098d0fdf1a: Pull complete
d3c2fac25849: Pull complete
3910b894cabf: Pull complete
e264eba4146d: Pull complete
f2690d75a5da: Pull complete
a974cf3861bb: Pull complete
4931f440d283: Pull complete
79ac8791bae4: Pull complete
Digest: sha256:a24e6faa2337f4deaa903eb9036f01387414252d3f6649f0bfe471b897310c63
Status: Downloaded newer image for 192.168.56.101:30211/creationline/logstash:7.13.3
192.168.56.101:30211/creationline/logstash:7.13.3

ついでにbobもログアウトして、匿名ユーザとしてMSRからプルを試みてみましょう。

% docker logout 192.168.56.101:30211
Removing login credentials for 192.168.56.101:30211
% docker image rm 192.168.56.101:30211/creationline/logstash:7.13.3
Untagged: 192.168.56.101:30211/creationline/logstash:7.13.3
Untagged: 192.168.56.101:30211/creationline/logstash@sha256:a24e6faa2337f4deaa903eb9036f01387414252d3f6649f0bfe471b897310c63
Deleted: sha256:289a765558773dc7dd973c3d04680c0d0ceddafc2584034ad02473460d7342dd
Deleted: sha256:45cec05d815e103ec7ba60b0eb474609e8473539be5923edf7e87b5bfe18a612
Deleted: sha256:3c713164fa71c59dafe691e6a46400bc73f661169c59ccfc5098cdd082188a9c
Deleted: sha256:5c34fc522aa05f888841ea414476fcd6720fc487c6b48b18abdc07b37356e4eb
Deleted: sha256:56da02333067501293ef10bbf8bc5372ff688517f3e033a4698202bddbbae281
Deleted: sha256:dd7f5008374c9cc9352db5a678281473e09e4d4353d7648b5621e1b3de43b80c
Deleted: sha256:6e788512c81251cc71dbb7c1bb5ccaa3dc68ed6dd54df53645ad41c3e431558e
Deleted: sha256:4b8fd99c44be485efecea68c665517b4c201b5dd994b4a73b060cc5bb8b409c7
Deleted: sha256:01771379ef4293584918fe3e7ac03cd9914f9340b9bf4ecc16e73f7bcb96a32b
Deleted: sha256:0e5d2b96638ea2d7a72eb2a4bbed989d4b51e669ec762789c990a81df92e7025
Deleted: sha256:ef3bce480b0f7489a66c12559f64f6155c7031261d58e6dda91f438aa7a79969
Deleted: sha256:5b0b92f3990e2772d58c559c866d08436febf471792ad8502fce1f6ef568449e
% docker image pull 192.168.56.101:30211/creationline/logstash:7.13.3
Error response from daemon: pull access denied for 192.168.56.101:30211/creationline/logstash, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

想定通り、MSRにログインしていない匿名ユーザではプルに失敗しました。

このように、MSRではレポジトリに対するアクセス制限を行う機能を備えているため、さまざまなユーザやチームが同一のプライベートレジストリを利用して共同作業する際に非常に強力な選択肢となりうるでしょう。

MSRのレポジトリを他のKubernetesから利用

では、実際にMSRのレポジトリを他のKubernetesクラスタから利用してみます。ここではホストマシン上にkindでKubernetesを準備し、そちらで実施します。kindのインストール等については省略します。

まず、次のYAMLマニフェストを準備し、kindのKubernetesクラスタがMSRを信頼するようにします。

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.56.101:30211".tls]
insecure_skip_verify = true

このYAMLマニフェストを使ってインストールします。

% kind version
kind v0.11.1 go1.16.4 linux/amd64
% kind create cluster --config kind.yaml
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.21.1)
✓ Preparing nodes
✓ Writing configuration
✓ Starting control-plane
✓ Installing CNI
✓ Installing StorageClass
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Not sure what to do next?  Check out https://kind.sigs.k8s.io/docs/user/quick-start/
% kubectl cluster-info --context kind-kind
Kubernetes control plane is running at https://127.0.0.1:40377
CoreDNS is running at https://127.0.0.1:40377/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
% kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane,master 109s v1.21.1

ではこのKubernetes上に、MSRからイメージをプルしてPodを起動してみましょう。ログインが必要なコンテナイメージレジストリからプルするにはSecretを使います。詳細はCreate a Secret by providing credentials on the command lineを参照してください。

まず、MSRのWebUIにbobユーザでログインします。左のサイドバーの bob をクリックし、Profileを開き、上のAccess Tokensタブを開きます。

New access tokenボタンをクリックします。ここではDescriptionに「kind」と入力し、Createボタンをクリックします。

表示されたアクセストークンをどこか安全な場所に保存しておきます。二度と表示されないので注意しましょう。このアクセストークンはパスワードの代わりとして利用できます。ただしWebUIのログイン用には使えません。

このアクセストークンをKubernetesのSecretとして保存します。

% kubectl create secret docker-registry bob-msr --docker-server=https://192.168.56.101:30211/ --docker-username=bob --docker-password=cd22a466-4fd5-43d7-9e0a-d27d0380ca9c
secret/bob-msr created

次のYAMLマニフェストでPodを作成します。

apiVersion: v1
kind: Pod
metadata:
name: bob-logstash
spec:
restartPolicy: Never
containers:
- image: 192.168.56.101:30211/creationline/logstash:7.13.3
name: logstash
command: [ "echo", "hello-world" ]
imagePullSecrets:
- name: bob-msr

では、このPodをデプロイしてみましょう。

% kubectl apply -f bob-msr.yaml
pod/bob-logstash created
% kubectl get pod
NAME READY STATUS RESTARTS AGE
bob-logstash 0/1 Completed 0 13s
% kubectl logs bob-logstash
hello-world

想定通りに動作していることが確認できました。念のためdescribeの結果も見てみましょう。

% kubectl describe pod bob-logstash
Name: bob-logstash
Namespace: default
Priority: 0
Node: kind-control-plane/172.20.0.2
Start Time: Wed, 05 Jan 2022 15:14:20 +0900
Labels:
Annotations:
Status: Succeeded
IP: 10.244.0.6
IPs:
IP: 10.244.0.6
Containers:
logstash:
Container ID: containerd://9f25e41641b7cc77a321e111eeee954047d7d75e72c4d3087420fa97aab09ce2
Image: 192.168.56.101:30211/creationline/logstash:7.13.3
Image ID: 192.168.56.101:30211/creationline/logstash@sha256:a24e6faa2337f4deaa903eb9036f01387414252d3f6649f0bfe471b897310c63
Port:
Host Port:
Command:
echo
hello-world
State: Terminated
Reason: Completed
Exit Code: 0
Started: Wed, 05 Jan 2022 15:14:22 +0900
Finished: Wed, 05 Jan 2022 15:14:22 +0900
Ready: False
Restart Count: 0
Environment:
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-sn5bz (ro)
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-sn5bz:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional:
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors:
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 109s default-scheduler Successfully assigned default/bob-logstash to kind-control-plane
Normal Pulling 108s kubelet Pulling image "192.168.56.101:30211/creationline/logstash:7.13.3"
Normal Pulled 65s kubelet Successfully pulled image "192.168.56.101:30211/creationline/logstash:7.13.3" in 42.99685449s
Normal Pulled 6s kubelet Container image "192.168.56.101:30211/creationline/logstash:7.13.3" already present on machine
Normal Created 4s kubelet Created container logstash
Normal Started 4s kubelet Started container logstash

MSRからイメージをプルしてPodを起動していることがわかります。

MSRの脆弱性スキャンとその応用例

さて、先ほどMSRにプッシュした logstash:7.13.3 の脆弱性スキャンもそろそろ終わっているころでしょう。WebUIから確認してみましょう。

Vulnerabilitiesが更新されて、Criticalが14、Highが95もあることがわかります。View detailsをクリックして詳細を見てみます。

このように、2021年末に話題となったlog4shellの脆弱性もきちんと検出できています。MSRの脆弱性スキャンを利用することで危険なイメージを発見するだけでなく、使用を禁止することも可能です。
例えば、「Criticalな脆弱性の数が3未満であればプルを許可する」というポリシーを定義してみます。

この状態で logstash:7.13.3 をプルしてみましょう。

% docker image pull 192.168.56.101:30211/creationline/logstash:7.13.3
Error response from daemon: unknown: pull access denied against creationline/logstash: enforcement policies '381ef99c-d2c9-4a8e-a45f-42812d18c763' blocked request

このように、ポリシー違反のためプルに失敗しました。ポリシーの設定次第で、安全なイメージのみをコンテナあるいはPodとして利用できるようになります。

まとめ

本稿では、MKEから独立して利用できるようになったMirantis Secure Registry (MSR)バージョン3.0.0を、Vagrant/Virtualbox上にk0sでインストールした1ノードKubernetesにデプロイし、ユーザ・チーム・オーガニゼーションを作成してアクセス制御機能を簡単に確認し、MSRにプッシュしたイメージを別のKubernetesで利用してみました。またMSRの目玉機能の一つである脆弱性スキャンによるイメージの利用可能ポリシーの設定も簡単に見てみました。
「脆弱性スキャンやユーザ管理は行いたいが、Docker Hubではなく自社インフラ内にイメージレジストリを置きたい」「MSRの機能は魅力だが、既にKubernetesクラスタを持っているので、MKE必須では難しい」といったニーズを満たせる製品となったと思います。
ご覧いただいたように、k0s上にも簡単にデプロイできるので、気軽にお試しいただくことも可能です。
製品に関する質問や価格、ライセンス体系などにつきましてはこちらからお問い合わせください。

Author

Chef・Docker・Mirantis製品などの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA