Automatic Docker image scan on push
Note
You can enable auto scans of Docker images for vulnerabilities on push to Container Registry in the vulnerability scanner settings without creating any Yandex Cloud Functions functions and triggers.
In this tutorial, you will create a Container Registry registry to store a Docker image. You will also set up automatic scanning for vulnerabilities to be done when pushing an image to the registry. A Cloud Functions trigger will track changes to the registry and invoke a function to start scanning when you push a Docker image to the registry.
To set up automatic Docker image scan on push:
- Prepare your cloud.
- Prepare the environment.
- Create a function.
- Create a trigger.
- Push the Docker image.
- Check the result.
If you no longer need the resources you created, delete them.
You can also deploy an infrastructure for automatic scanning of Docker images on push via Terraform using a ready-made configuration file.
Getting started
Sign up for Yandex Cloud and create a billing account:
- Go to the management console
and log in to Yandex Cloud or create an account if you do not have one yet. - On the Billing
page, make sure you have a billing account linked and it has theACTIVE
orTRIAL_ACTIVE
status. If you do not have a billing account, create one.
If you have an active billing account, you can go to the cloud page
Learn more about clouds and folders.
Prepare the environment
If you do not have the Yandex Cloud command line interface yet, install and initialize it.
-
Install
Docker. -
Create a registry to push a Docker image to.
Management consoleCLITerraformAPI- In the management console
, select the folder to create your registry in. - In the list of services, select Container Registry.
- Click Create registry.
- Specify a name for the registry.
- Click Create registry.
Run this command:
yc container registry create --name my-reg
Result:
done id: crpd50616s9a******** folder_id: b1g88tflru0e******** name: my-reg status: ACTIVE created_at: "2019-01-09T14:34:06.601Z"
- In the management console
-
Create a service account named
scanner
and assign it thecontainer-registry.images.scanner
role for the folder where you created the registry.Management consoleCLITerraformAPI- In the management console
, select a folder to create a service account in. - At the top of the screen, go to the Service accounts tab.
- Click Create service account.
- Enter a name for the service account.
- Click Add role and select
container-registry.images.scanner
. - Click Create.
-
Create a service account:
yc iam service-account create --name service-acc
Result:
id: ajelabcde12f******** folder_id: b0g12ga82bcv******** created_at: "2021-05-17T14:32:18.900092Z" name: service-acc
-
Assign the role to the service account:
yc resource-manager folder add-access-binding <folder_id> \ --role container-registry.images.scanner \ --subject serviceAccount:<service_account_ID>
Use the create method for the ServiceAccount resource and updateAccessBindings for Folder.
- In the management console
-
Repeat the steps to create a service account named
invoker
and assign it thefunctions.functionInvoker
role for the folder where you created the registry. -
Install Docker.
Create a function
In Cloud Functions, create a function named scan-on-push
that will run the Docker image scan:
- In the management console
, select the folder where you want to create a function. - Select Cloud Functions.
- Click Create function.
- Enter
scan-on-push
as the function name, and add a function description. - Click Create.
- Go to Editor and create a version of the function:
- Under Function code:
-
Select the
Bash
runtime environment and click Continue. -
Select how you want to edit the function:
Code editor
. -
In the function edit window, click Create file. In the window that opens, enter
handler.sh
as the file name and click Create. -
Copy the following code to the
handler.sh
file:DATA=$(cat | jq -sr '.[0].messages[0].details') ID=$(echo $DATA | jq -r '.image_id') NAME=$(echo $DATA | jq -r '.repository_name') TAG=$(echo $DATA | jq -r '.tag') yc container image scan --id ${ID} --async 1>&2
-
Specify the entry point:
handler.sh
.
-
- Under Parameters, specify:
- Timeout, sec:
60
- Memory:
128 MB
- Service account:
Scanner
- Timeout, sec:
- Click Save changes.
- Under Function code:
-
Create a function named
scan-on-push
:yc serverless function create --name=scan-on-push
Result:
id: d4ejb1799eko******** folder_id: aoek49ghmknn******** created_at: "2021-17-05T14:07:32.134Z" name: scan-on-push log_group_id: eolm8aoq9vcp******** http_invoke_url: https://functions.yandexcloud.net/d4ejb1799eko******** status: ACTIVE
-
Create the
handler.sh
file and paste the following code to it:DATA=$(cat | jq -sr '.[0].messages[0].details') ID=$(echo $DATA | jq -r '.image_id') NAME=$(echo $DATA | jq -r '.repository_name') TAG=$(echo $DATA | jq -r '.tag') yc container image scan --id ${ID} --async 1>&2
-
Create a version of the
scan-on-push
function:yc serverless function version create \ --function-name=scan-on-push \ --runtime bash \ --entrypoint handler.sh \ --memory 128m \ --execution-timeout 60s \ --source-path handler.sh \ --service-account-id <service_account_ID>
Where:
--function-name
: Name of the function you want to create a version of.--runtime
: Runtime environment.--entrypoint
: Entry point specified in the<function_file_name>.<handler_name>
format.--memory
: Amount of RAM.--execution-timeout
: Maximum function execution time before the timeout is reached.--source-path
: File with the function code.--service-account-id
: Service account ID.
Result:
done (1s) id: d4egi3pmsd1q******** function_id: d4e275oj7jtp******** ... tags: - $latest log_group_id: ckg6nb0c7uf1********
Use the create and the createVersion methods for the Function resource.
Create a trigger
Create a trigger that will invoke your function when creating a tag of the Docker image.
- In the management console
, select the folder where you want to create your trigger. - Select Cloud Functions.
- Go to the Triggers tab.
- Click Create trigger.
- Under Basic settings:
- Enter a name and description for the trigger.
- In the Type field, select
Container Registry
.
- Under Container Registry settings:
- In the Registry field, select the registry to push the Docker image to.
- In the Event types field, select the
Create Docker image tag
event.
- Under Function settings:
- Select the
scan-on-push
function. - Specify the function version tag:
$latest
. - Specify the
invoker
service account to invoke the function.
- Select the
- Click Create trigger.
To create a trigger, run the command:
yc serverless trigger create container-registry \
--name <trigger_name> \
--registry-id <registry_ID> \
--events 'create-image-tag' \
--invoke-function-id <function_ID> \
--invoke-function-service-account-id <service_account_ID>
Where:
--name
: Trigger name.--registry-id
: ID of the registry to push the Docker image to.--events
: Events activating the trigger.--invoke-function-id
: Function ID.--invoke-function-service-account-id
: ID of the service account with rights to invoke the function.
Result:
id: a1spt834cjmk********
folder_id: b1g86q4m5vej********
created_at: "2021-05-18T20:42:54.898949653Z"
...
function_tag: $latest
service_account_id: aje1insoe23e********
status: ACTIVE
Push the Docker image
-
Run Docker Desktop.
-
Log in to the registry under your username:
Using the Docker credential helperUsing an OAuth tokenUsing an Yandex Identity and Access Management token-
Configure Docker to use
docker-credential-yc
:yc container registry configure-docker
Result:
Credential Helper is configured in '/home/<user>/.docker/config.json'
Settings are saved in the current user's profile.
Warning
Credential Helper only works when using Docker without
sudo
. Configuring Docker to run as the current user without usingsudo
is described in the official Docker documentation . -
Make sure that Docker is configured.
The
/home/<user>/.docker/config.json
configuration file must include the following line:"cr.yandex": "yc"
-
You can now use Docker, for example, to push Docker images. You do not need to run the
docker login
command for that.
-
If you do not have an OAuth token yet, get one by following this link
. -
Run this command:
echo <OAuth_token> | docker login --username oauth --password-stdin cr.yandex
Result:
Login succeeded
Note
The IAM token has a short lifetime — no more than 12 hours. That's why this is a good method for applications that automatically request an IAM token.
-
Run this command:
yc iam create-token | docker login --username iam --password-stdin cr.yandex
Result:
Login succeeded
-
-
Pull a Docker image from Docker Hub
:docker pull ubuntu:20.04
Result:
20.04: Pulling from library/ubuntu Digest: sha256:cf31af331f38d1d7158470e095b132acd126a7180a54f263d386da88******** Status: Image is up to date for ubuntu:20.04 docker.io/library/ubuntu:20.04
-
Assign a tag to the Docker image:
docker tag ubuntu:20.04 cr.yandex/<registry_ID>/ubuntu:20.04
-
Push the Docker image to Container Registry:
docker push cr.yandex/<registry_ID>/ubuntu:20.04
Result:
The push refers to repository [cr.yandex/crpu20rpdc2f********/ubuntu] 2f140462f3bc: Layer already exists 63c99163f472: Layer already exists ccdbb80308cc: Layer already exists 20.04: digest: sha256:86ac87f73641c920fb42cc9612d4fb57b5626b56ea2a19b894d0673f******** size: 943
Check the result
-
View the
scan-on-push
function logs and make sure that it was executed.Management consoleCLI- In the management console
, select Cloud Functions. - Go to the Functions section and select the
scan-on-push
function. - In the window that opens, go to Logs and specify the time period. The default time period is 1 hour.
To find out the name or unique ID of a function, get a list of functions in the folder.
View the function execution log:
yc serverless function logs scan-on-push
Result:
2021-05-18 09:27:43 START RequestID: 34dc9533-ed6e-4468-b9f2-2aa0******** Version: b09i2s85a0c1******** 2021-05-18 09:27:43 END RequestID: 34dc9533-ed6e-4468-b9f2-2aa0******** 2021-05-18 09:27:43 REPORT RequestID: 34dc9533-ed6e-4468-b9f2-2aa0******** Duration: 538.610 ms Billed Duration: 538.700 ms Memory Size: 128 MB Max Memory Used: 13 MB 2021-05-18 09:29:25 START RequestID: 5b6a3779-dcc8-44ec-8ee2-2e7f******** Version: b09i2s85a0c1******** 2021-05-18 09:29:26 END RequestID: 5b6a3779-dcc8-44ec-8ee2-2e7f******** 2021-05-18 09:29:26 REPORT RequestID: 5b6a3779-dcc8-44ec-8ee2-2e7f******** Duration: 554.904 ms Billed Duration: 555.000 ms Memory Size: 128 MB Max Memory Used: 13 MB ...
- In the management console
-
Make sure that a new scan started when you pushed the Docker image.
Management consoleCLI- In the management console
, select the parent folder for the registry containing the Docker image. - Select Container Registry.
- Select the registry where you pushed your Docker image.
- Open the repository with the Docker image.
- Select the relevant Docker image and check the Date of last scan parameter value.
To view scans by Docker image, run the command:
yc container image list-scan-results --repository-name=<registry_ID>/<Docker_image_name>
Result:
+----------------------+----------------------+---------------------+--------+--------------------------------+ | ID | IMAGE | SCANNED AT | STATUS | VULNERABILITIES | +----------------------+----------------------+---------------------+--------+--------------------------------+ | crpu20rpdc2f******** | crpqmsqp5mtb******** | 2021-05-18 14:34:02 | READY | medium:6, low:13, negligible:3 | +----------------------+----------------------+---------------------+--------+--------------------------------+
- In the management console
How to delete the resources you created
To stop paying for the resources you created:
- Delete the Docker image stored in Yandex Container Solution, as well as the registry.
- Delete the Cloud Functions function.
- Delete the Cloud Functions trigger.
How to create an infrastructure using Terraform
Terraform
For more information about the provider resources, see the documentation on the Terraform
If you change the configuration files, Terraform automatically detects which part of your configuration is already deployed, and what should be added or removed.
To set up automatic Docker image scan on push using Terraform:
-
Specify the source for installing the Yandex Cloud provider (see Configure a provider, step 1).
-
Prepare files with the infrastructure description:
Ready-made archiveManually- Create a directory for files.
- Download the archive
(2 KB). - Unpack the archive to the directory. As a result, it should contain the
image-auto-scan.tf
configuration file, theimage-auto-scan.auto.tfvars
file with user data, and thefunction.zip
archive with the function code.
-
Create a directory for configuration files.
-
In the directory, create a configuration file named
image-auto-scan.tf
:image-auto-scan.tf# Declaring variables for user-defined parameters variable "zone" { type = string } variable "folder_id" { type = string } # Adding other variables locals { sa_scanner_name = "scanner" sa_invoker_name = "invoker" registry_name = "my-registry" function_name = "scan-on-push" } # Specifying provider settings terraform { required_providers { yandex = { source = "yandex-cloud/yandex" version = ">= 0.47.0" } } } provider "yandex" { folder_id = var.folder_id } # Creating service accounts resource "yandex_iam_service_account" "scanner" { name = local.sa_scanner_name description = "SA for Container Registry" folder_id = var.folder_id } resource "yandex_iam_service_account" "invoker" { name = local.sa_invoker_name description = "SA for Cloud Functions" folder_id = var.folder_id } # Assigning roles to service accounts resource "yandex_resourcemanager_folder_iam_member" "sa-role-scanner" { folder_id = var.folder_id role = "container-registry.images.scanner" member = "serviceAccount:${yandex_iam_service_account.scanner.id}" } resource "yandex_resourcemanager_folder_iam_member" "sa-role-invoker" { folder_id = var.folder_id role = "functions.functionInvoker" member = "serviceAccount:${yandex_iam_service_account.invoker.id}" } # Creating a container registry resource "yandex_container_registry" "my-reg" { name = local.registry_name folder_id = var.folder_id } # Creating a function resource "yandex_function" "test-function" { name = local.function_name user_hash = "my-first-function" runtime = "bash" entrypoint = "handler.sh" memory = "128" execution_timeout = "60" service_account_id = yandex_iam_service_account.scanner.id content { zip_filename = "function.zip" } }
-
Create the
image-auto-scan.auto.tfvars
file with user data:image-auto-scan.auto.tfvarszone = "<availability_zone>" folder_id = "<folder_ID>"
-
Prepare a ZIP archive with the function code.
-
Create the
handler.sh
file and paste the following code to it:handler.shWarning
DATA=$(cat | jq -sr '.[0].messages[0].details') ID=$(echo $DATA | jq -r '.image_id') NAME=$(echo $DATA | jq -r '.repository_name') TAG=$(echo $DATA | jq -r '.tag') yc container image scan --id ${ID} --async 1>&2
-
Create a ZIP archive named
function.zip
with thehandler.sh
file.
-
For more information about the parameters of resources used in Terraform, see the provider documentation:
-
In the
image-auto-scan.auto.tfvars
file, set the user-defined parameters:zone
: Availability zone to create the infrastructure in.folder_id
: ID of the folder to create the infrastructure in.
-
Create resources:
-
In the terminal, change to the folder where you edited the configuration file.
-
Make sure the configuration file is correct using the command:
terraform validate
If the configuration is correct, the following message is returned:
Success! The configuration is valid.
-
Run the command:
terraform plan
The terminal will display a list of resources with parameters. No changes are made at this step. If the configuration contains errors, Terraform will point them out.
-
Apply the configuration changes:
terraform apply
-
Confirm the changes: type
yes
in the terminal and press Enter.
-