Sysdig Secureのレジストリスキャンを試してみた（Azure Container Registry編）

これまでにSysdig Secureのレジストリスキャンとして下記をご紹介してきました。今回はAzure Container Registry（ACR）に対するレジストリスキャンの設定方法をご紹介します。

https://qiita.com/yotake/items/3ae8aba6659434f32f54

https://qiita.com/yotake/items/da3ad6740795468f30f7

https://qiita.com/yotake/items/d98142b8004d1784dccc

Sysdig SecureでACRに対するレジストリスキャンを実施する方法として、レジストリトークンを使用する方法とサービスプリンシパルを使用する２つの方法があります。

# （オプション１） レジストリトークンの作成

:::note
レジストリトークンは `_repositories_admin`のスコープマップを使用する必要があります。詳細は<a href=https://learn.microsoft.com/en-us/azure/container-registry/container-registry-repository-scoped-permissions>Azure ACRのドキュメント</a>をご参照ください。
:::
Azureコンソールで自身のレジストリのTokens画面に移動します。__Add__ ボタンをクリックしてトークンを新規作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/75a70539-c2b3-d181-a19b-737d520b8604.png)
トークン名を入力し、Scope mapとして`_repositories_admin`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/c601a78d-e0fe-e98b-de86-4ac15667b3e8.png)
トークンが作成されたことを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/cb234489-9111-dc60-98ad-fb6dfe65dceb.png)
作成されたトークン名をクリックして詳細画面に移動し、__password1__ をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/05fca632-4b65-5d91-eec0-301da7b5b39b.png)
__Generate__ をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/eadbd63b-8395-0005-2367-69b8d748d76f.png)
Passwordが生成されるので、後で使用するためにコピーしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/8f4f05ab-8fcd-380a-82e1-251f63e9ae25.png)

# （オプション２） サービスプリンシパルの作成
https://learn.microsoft.com/en-us/cli/azure/azure-cli-sp-tutorial-1?tabs=bash

:::note
ACRに対してサービスプリンシパルを使ったレジストリスキャンを実施するためには、<a href=https://learn.microsoft.com/en-us/azure/container-registry/container-registry-auth-service-principal>Azure ACRのドキュメント</a>の指示にしたがって`acrpull`ロールを持ち、スコープを自身のレジストリに設定したサービスプリンシパルを作成する必要があります。
:::
以下のCLIコマンドでサービルプリンシパルを作成します。
```
az ad sp create-for-rbac --name yoSP --role acrpull --scopes /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/RegistryRG 
```

# LinuxサーバへのAzure CLIのインストール

:::note
Azure Cloud ShellにDockerはインストールできないので注意。
:::
すでにDockerをインストール済みのLinuxサーバにログインし、以下のコマンドを実行します。
```
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
```

# ACRへのイメージプッシュ
Azureにログインします。
```
az login
```
ACRにログインします。
```
az acr login --name yoregistry
```
イメージをプルしてタグを付けます。

:::note
古すぎるイメージはレジストリスキャンがスキップされるので使用しないでください（デフォルトではスキャン対象は作成から90日間までに設定されています）。
:::

```
docker pull nginx
docker tag docker.io/library/nginx yoregistry.azurecr.io/samples/nginx
```
レジストリトークンを使用してACRに`docker login`します。
```
docker login -u yoRegistryToken -p <REGISTRY_TOKEN> yoregistry.azurecr.io
```
イメージをACRにプッシュします。
```
docker push yoregistry.azurecr.io/samples/nginx
```
Azureコンソールでイメージがプッシュされたことを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/2b8fdf5a-5487-e10e-62a4-9b277e0a7cc1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/f000dfe3-37ce-e9b6-d5bb-122435149c66.png)

# レジストリスキャナーのデプロイ

:::note
レジストリスキャナーはAzure Kubernetes Service (AKS)などのKubernetes上にデプロイする必要があります。
:::
Helmコマンドを使ってKubernetes上にレジストリスキャナーをデプロイします。

（オプション1）レジストリトークンを認証に使用する場合
```
helm upgrade --install registry-scanner sysdig/registry-scanner --version=1 \
--set config.secureBaseURL=<SYSDIG_SECURE_URL> \
--set config.secureAPIToken=<SYSDIG_SECURE_API_TOKEN> \
--set config.registryType=acr \
--set config.registryURL=<ACR_REGISTRY_URL> \
--set config.registryUser=<ACR_TOKEN_NAME> \
--set config.registryPassword=<ACR_TOKEN_PASSWORD>
```

（オプション２）サービスプリンシパルを認証に使用する場合
```
$ helm upgrade --install registry-scanner sysdig/registry-scanner --version=1 \
--set config.secureBaseURL=<SYSDIG_SECURE_URL> \
--set config.secureAPIToken=<SYSDIG_SECURE_API_TOKEN> \
--set config.registryType=acr \
--set config.registryURL=<ACR_REGISTRY_URL> \
--set config.registryUser=<SP_ID> \
--set config.registryPassword=<SP_PASSWORD>
```

:::note
SYSDIG_SECURE_URLの値はご利用のSysdig SaaSリージョンにより異なります。詳細は下記ドキュメントをご参照ください。
https://docs.sysdig.com/en/docs/administration/saas-regions-and-ip-ranges
:::
インストールログは以下のようになります。
```
Release "registry-scanner" does not exist. Installing it now.
NAME: registry-scanner
LAST DEPLOYED: Sun Jan 21 03:15:16 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
# ============================================================================================
#  Info: Chart version 1.1.28 has been installed with Registry Scanner 0.2.63.
#
#        The chart version follows SemVer (https://semver.org/), you can pin
#        the chart version with --version=1, --version=1.0, --version=1.0.0, etc.
#        Using --version=1 will ensure that you get the latest 1.x.x version, but
#        you don't get any breaking changes when updating.
#
#        For more information, or you are still using the legacy scanning engine,
#        please check the documentation:
#        > https://docs.sysdig.com/en/docs/installation/container-registry-scanner/
# ============================================================================================
#
# Cronjob 'registry-scanner' with schedule: "0 6 * * 6" has been created to perform a periodical scan.
# For troubleshooting, check the status of the jobs and logs of the 'registry-scanner' pods that are created on every execution.
#
```
下記コマンドで、Cronjobとして登録されていることを確認します。
デフォルトでは毎週土曜日の午前6時にスキャンが実行される設定で登録されています。
```
$ kubectl get cronjob
NAME               SCHEDULE    SUSPEND   ACTIVE   LAST SCHEDULE   AGE
registry-scanner   0 6 * * 6   False     0        <none>          112s
```

# レジストリスキャンの実行
１度だけ手動でレジストリスキャンを実行するにはCronjobからjobを作成します。
```
kubectl create job --from=cronjob/registry-scanner registry-scanner-manual
```
jobとして作成されたPod名を確認します。
```
kubectl get po
```
スキャンログを確認します。
```
$ kubectl logs registry-scanner-manual-xtsrs -f
{"level":"info","component":"main","time":"2024-01-21T03:27:38Z","message":"Starting registry-scanner version v0.2.63-2024-01-19T09:31:20Z"}
{"level":"info","component":"config","time":"2024-01-21T03:27:38Z","message":"reading config from: /config.yaml"}
{"level":"info","component":"main","time":"2024-01-21T03:27:38Z","message":"##################   configuration  ######################"}
{"level":"info","component":"main","time":"2024-01-21T03:27:38Z","message":"Sysdig secure config, Secure URL: https://us2.app.sysdig.com"}
{"level":"info","component":"main","time":"2024-01-21T03:27:38Z","message":"scanner config, with workers: 5"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:38Z","message":"credentials provider type: 'acr'"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:38Z","message":"using ACR credentials provider"}
{"level":"info","component":"init-newvmscanner-k8s-job","time":"2024-01-21T03:27:38Z","message":"Creating new vulnerability-management scanner (K8s job)"}
{"level":"info","component":"init-newvmscanner-k8s-job","time":"2024-01-21T03:27:38Z","message":"Scanner Options: map[disableplatformscanning:false jobs:map[resources:map[limits:map[memory:2Gi] requests:map[cpu:500m memory:500Mi]] serviceaccountname:registry-scanner temporaryvolumesizelimit:2Gi ttlsecondsafterfinished:600] k8sincluster:true labels:map[] namespace:default runner:new-vm-scanner-k8s-job securitycontext:map[] workers:5] "}
{"level":"info","component":"init-newvmscanner-k8s-job","time":"2024-01-21T03:27:39Z","message":"Backend scanning is enabled: will scan on backend"}
{"level":"info","component":"init-newvmscanner-k8s-job","time":"2024-01-21T03:27:39Z","message":"Schedule expression provided: 0 6 * * 6"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"registry client type: 'acr'"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"using ACR registry client"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"registry url: 'yoregistry.azurecr.io'"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"registry api: 'yoregistry.azurecr.io'"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"registry user: 'yoRegistryToken'"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"registry password: <redacted>"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"registry skip tls errors: false"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"warning, could not parse ClientPageSize value "}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"general filter, include: []"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"general filter, exclude: []"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"general filter, max age days: 90 "}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"general filter, max tags per repository: 5"}
{"level":"info","component":"factory","time":"2024-01-21T03:27:39Z","message":"general filter, max repositories per registry: 500"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:39Z","message":"##############  metadata fetching phase  #################"}
{"level":"info","component":"google-client","time":"2024-01-21T03:27:39Z","message":"Limiting to 500 repositories (filter.maxRepositoriesPerRegistry setting)"}
{"level":"info","component":"google-client","time":"2024-01-21T03:27:39Z","message":"discovered total number of repositories: 1"}
{"level":"info","component":"google-client","time":"2024-01-21T03:27:39Z","message":"parallelized. 1/1, getting tags concurrently from repository samples/nginx"}
{"level":"info","component":"repo-retriever-info-filler","time":"2024-01-21T03:27:40Z","message":"1 images found"}
{"level":"info","component":"repo-retriever-info-filler","time":"2024-01-21T03:27:40Z","message":"fetching found images configuration in parallel (this may take a while)"}
{"level":"info","component":"image-retriever-repos","time":"2024-01-21T03:27:41Z","message":"after filters were applied, 1 images are going to be scanned"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"metadata fetching phase finished. took 1 second 782 milliseconds 943 microseconds"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"Total images with errors in metadata phase: 0"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"After metadata phase, from 1 images, 1 will be scanned"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"################   scanning phase  ######################"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"Creating registry report. Num. of workers: 5"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"Total selected images to be scanned: 1"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"progression status | 0.00%  completed | completed images: 0/1 | Elapsed: 35.319µs | ETA: ?"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"progression status | processed 0/1 images (0 errors)"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"returning due to queue is closed"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"returning due to queue is closed"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"returning due to queue is closed"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:41Z","message":"returning due to queue is closed"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:51Z","message":"yoregistry.azurecr.io/samples/nginx:latest, (job registry-scanner-worker-w24t4-1) scan completed with status: success"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:51Z","message":"returning due to queue is closed"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:51Z","message":"scanning phase finished. Total selected images to be scanned: 1 = Success: 1 + Failed: 0 + Skipped: 0. took 10 seconds 557 milliseconds 276 microseconds"}
{"level":"info","component":"report-builder","time":"2024-01-21T03:27:51Z","message":"################   registry image scanning finished  ######################"}
```
Sysdig UIでレジストスキャンのスキャン結果を確認できます。
__Vulnerabilities > Registry__ に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2802821/2eacd7e2-e775-0256-5305-5ffcf4516989.png)

# まとめ
registry-scannerを使って、Azure Container Registryのレジストリスキャンを簡単に実行できることが確認できました。