Сканирование уязвимостей Container Registry при непрерывном развертывании приложений с помощью GitLab
Вы можете сканировать уязвимости Docker-образов в Yandex Container Registry при непрерывном развертывании приложений Yandex Managed Service for Kubernetes через GitLab.
Для этого с помощью непрерывной интеграции (Continuous Integration, CI) в GitLab создается специальный сценарий, который запускается после каждого коммита:
- Сборка приложения в Docker-образ и загрузка образа в Container Registry.
- Сканирование Docker-образа в Container Registry на наличие уязвимостей.
- Развертывание приложения из Docker-образа в кластере Managed Service for Kubernetes с помощью инструментов Yandex Cloud.
Чтобы настроить сканер уязвимостей:
- Создайте инстанс GitLab.
- Настройте GitLab.
- Создайте тестовое приложение.
- Создайте GitLab Runner.
- Настройте сценарий CI.
- Проверьте результат.
Если созданные ресурсы вам больше не нужны, удалите их.
Перед началом работы
Подготовьте инфраструктуру
Если у вас еще нет интерфейса командной строки Yandex Cloud, установите и инициализируйте его.
По умолчанию используется каталог, указанный в профиле CLI. Вы можете указать другой каталог с помощью параметра --folder-name
или --folder-id
.
-
Если у вас еще нет сети, создайте ее.
-
Если у вас еще нет подсетей, создайте их в зонах доступности, где будут созданы кластер Yandex Managed Service for Kubernetes и группа узлов.
-
- Для ресурсов с ролью editor на каталог, в котором создается кластер Managed Service for Kubernetes. От его имени будут создаваться ресурсы, необходимые кластеру Managed Service for Kubernetes.
- Для узлов с ролями container-registry.images.puller и container-registry.images.pusher на каталог с реестром Docker-образов. От его имени узлы Managed Service for Kubernetes будут загружать в реестр собранные в GitLab Docker-образы, а также скачивать их для запуска подов.
Совет
Вы можете использовать один и тот же сервисный аккаунт для обеих операций.
-
Создайте группы безопасности для кластера Managed Service for Kubernetes и входящих в него групп узлов.
Важно
От настройки групп безопасности зависит работоспособность и доступность кластера, а также запущенных в нем сервисов и приложений.
-
Создайте группу безопасности для работы инстанса Managed Service for GitLab.
-
Создайте кластер Managed Service for Kubernetes и группу узлов. При создании кластера Managed Service for Kubernetes укажите ранее созданные сервисные аккаунты для ресурсов и узлов и группы безопасности.
-
Создайте авторизованный ключ для сервисного аккаунта с ролью
container-registry.images.pusher
и сохраните в файлkey.json
:yc iam key create \ --service-account-name <имя_сервисного_аккаунта> \ --output key.json
Ключ необходим для доступа к реестру из GitLab.
-
Если у вас еще нет Terraform, установите его.
-
Получите данные для аутентификации. Вы можете добавить их в переменные окружения или указать далее в файле с настройками провайдера.
-
Настройте и инициализируйте провайдер. Чтобы не создавать конфигурационный файл с настройками провайдера вручную, скачайте его
. -
Поместите конфигурационный файл в отдельную рабочую директорию и укажите значения параметров. Если данные для аутентификации не были добавлены в переменные окружения, укажите их в конфигурационном файле.
-
Скачайте в ту же рабочую директорию файл конфигурации k8s-and-registry-for-gitlab.tf
.В этом файле описаны:
-
Сеть.
-
Сервисный аккаунт, необходимый для работы кластера Managed Service for Kubernetes и группы узлов.
-
Группы безопасности, которые содержат необходимые правила для кластера Managed Service for Kubernetes и входящих в него групп узлов.
Важно
От настройки групп безопасности зависит работоспособность и доступность кластера, а также запущенных в нем сервисов и приложений.
-
Группа безопасности по умолчанию и правила, необходимые для работы инстанса Managed Service for GitLab.
-
Авторизованный ключ для сервисного аккаунта. Ключ необходим для доступа к реестру из GitLab.
-
Локальный файл
key.json
с данными авторизованного ключа.
-
-
Укажите в файле
k8s-and-registry-for-gitlab.tf
:- Идентификатор каталога.
- Версию Kubernetes для кластера Managed Service for Kubernetes и групп узлов.
- Имя сервисного аккаунта кластера Managed Service for Kubernetes.
- Имя реестра Container Registry.
-
Проверьте корректность файлов конфигурации Terraform с помощью команды:
terraform validate
Если в файлах конфигурации есть ошибки, Terraform на них укажет.
-
Создайте необходимую инфраструктуру:
-
Выполните команду для просмотра планируемых изменений:
terraform plan
Если конфигурации ресурсов описаны верно, в терминале отобразится список изменяемых ресурсов и их параметров. Это проверочный этап: ресурсы не будут изменены.
-
Если вас устраивают планируемые изменения, внесите их:
-
Выполните команду:
terraform apply
-
Подтвердите изменение ресурсов.
-
Дождитесь завершения операции.
-
В указанном каталоге будут созданы все требуемые ресурсы. Проверить появление ресурсов и их настройки можно в консоли управления
. -
Важно
Для реальных приложений доступ сервисных аккаунтов кластера Managed Service for Kubernetes к загрузке Docker-образов в реестр должен быть ограничен по соображениям безопасности. В этом случае создайте отдельный сервисный аккаунт с ролью container-registry.images.pusher
и указывайте его для развертывания приложений.
Установите дополнительные зависимости
Установите в локальном окружении:
- Утилиту потоковой обработки JSON-файлов
jq
. - Инструмент командной строки kubectl
и настройте его на работу с созданным кластером Managed Service for Kubernetes.
Создайте инстанс GitLab
Создайте инстанс Managed Service for GitLab или виртуальную машину с образом GitLab в той же облачной сети, где расположен кластер Managed Service for Kubernetes.
Создайте инстанс Managed Service for GitLab согласно инструкции.
Запустите GitLab на ВМ с публичным IP-адресом.
-
На странице каталога в консоли управления
нажмите кнопку Создать ресурс и выберитеВиртуальная машина
. -
В блоке Образ загрузочного диска в поле Поиск продукта введите
Gitlab
и выберите публичный образ GitLab. -
В блоке Расположение выберите зону доступности, в которой будет находиться ВМ. Если вы не знаете, какая зона доступности вам нужна, оставьте выбранную по умолчанию.
-
В блоке Вычислительные ресурсы перейдите на вкладку
Своя конфигурация
и укажите необходимую платформу, количество vCPU и объем RAM:- Платформа —
Intel Ice Lake
. - vCPU —
4
. - Гарантированная доля vCPU —
100%
. - RAM —
8 ГБ
.
- Платформа —
-
В блоке Сетевые настройки:
- В поле Подсеть выберите сеть и подсеть, к которым нужно подключить ВМ. Если нужной сети или подсети еще нет, создайте их.
- В поле Публичный адрес оставьте значение
Автоматически
, чтобы назначить ВМ случайный внешний IP-адрес из пула Yandex Cloud, или выберите статический адрес из списка, если вы зарезервировали его заранее.
-
В блоке Доступ выберите вариант SSH-ключ и укажите данные для доступа на ВМ:
- В поле Логин введите имя пользователя. Не используйте имя
root
или другие имена, зарезервированные ОС. Для выполнения операций, требующих прав суперпользователя, используйте командуsudo
. -
В поле SSH-ключ выберите SSH-ключ, сохраненный в вашем профиле пользователя организации.
Если в вашем профиле нет сохраненных SSH-ключей или вы хотите добавить новый ключ:
- Нажмите кнопку Добавить ключ.
- Задайте имя SSH-ключа.
- Загрузите или вставьте содержимое открытого SSH-ключа. Пару SSH-ключей для подключения к ВМ по SSH необходимо создать самостоятельно.
- Нажмите кнопку Добавить.
SSH-ключ будет добавлен в ваш профиль пользователя организации.
Если в организации отключена возможность добавления пользователями SSH-ключей в свои профили, добавленный открытый SSH-ключ будет сохранен только в профиле пользователя создаваемой виртуальной машины.
- В поле Логин введите имя пользователя. Не используйте имя
-
В блоке Общая информация задайте имя ВМ:
ci-tutorial-gitlab
. -
Нажмите кнопку Создать ВМ.
Создание ВМ может занять несколько минут. Когда ВМ перейдет в статус RUNNING
и запустится GitLab, настройте его.
Настройте GitLab
Чтобы настроить GitLab и подготовить процесс непрерывной интеграции (Continuous Integration, CI), создайте новый проект и введите параметры для авторизации в CI:
-
Авторизуйтесь в веб-интерфейсе инстанса Managed Service for GitLab.
-
Нажмите кнопку Create a project.
-
Нажмите кнопку Create blank project.
-
Заполните поля:
- Project name —
gitlab-test
. - Project URL — выберите пользователя-администратора в поле рядом с FQDN инстанса Managed Service for GitLab.
Остальные поля оставьте без изменений.
- Project name —
-
Нажмите кнопку Create project.
-
На странице сервиса Yandex Compute Cloud выберите созданную ВМ и скопируйте ее публичный IP-адрес.
-
Подключитесь к ВМ по протоколу SSH.
-
Получите пароль администратора GitLab с помощью команды ВМ:
sudo cat /etc/gitlab/initial_root_password
-
Скопируйте пароль из строки
Password
(исключая пробелы) в буфер обмена или отдельный файл. -
Откройте в браузере ссылку
http://<публичный_IP-адрес_ВМ>
. Откроется веб-интерфейс GitLab. -
Войдите в систему с учетной записью администратора:
- Username or email —
root
. - Password — пароль, скопированный ранее.
Если вы не можете войти, сбросьте пароль учетной записи администратора
. - Username or email —
-
Повторно войдите в систему с учетной записью администратора, используя новый пароль.
-
Выберите Create a project.
-
Задайте имя проекта:
gitlab-test
. -
Нажмите кнопку Create project.
Создайте тестовое приложение
Создайте тестовое приложение, которое можно будет развернуть в кластере Yandex Managed Service for Kubernetes:
- Добавьте в проект
Dockerfile
:-
Авторизуйтесь в GitLab.
-
Откройте проект GitLab.
-
В строке навигации по репозиторию нажмите кнопку
и в выпадающем меню выберите пункт New file. -
Назовите файл
Dockerfile
и добавьте в него код:FROM alpine:3.10 CMD echo "Hello"
-
Напишите комментарий к коммиту в поле Commit message:
Dockerfile for test application
. -
Нажмите кнопку Commit changes.
-
- Добавьте в проект манифест создания ресурсов кластера Managed Service for Kubernetes:
-
Откройте проект GitLab.
-
В строке навигации по репозиторию нажмите кнопку
и в выпадающем меню выберите пункт New file. -
Назовите файл
k8s.yaml
:k8s.yaml
apiVersion: v1 kind: Namespace metadata: name: hello-world --- apiVersion: apps/v1 kind: Deployment metadata: name: hello-world-deployment namespace: hello-world spec: replicas: 1 selector: matchLabels: app: hello template: metadata: namespace: hello-world labels: app: hello spec: containers: - name: hello-world image: __VERSION__ imagePullPolicy: Always
-
Напишите комментарий к коммиту в поле Commit message:
Docker image deployment config
. -
Нажмите кнопку Commit changes.
-
Создайте GitLab Runner
Чтобы запускать задачи сборки в кластере Yandex Managed Service for Kubernetes, создайте GitLab Runner
После установки вы можете запускать автоматизированные сборки внутри своего кластера Managed Service for Kubernetes.
Подробнее про установку и настройку GitLab Runner читайте в документации GitLab
Настройте аутентификацию Kubernetes в GitLab
Настроить аутентификацию в GitLab можно с помощью токена сервисного аккаунта Kubernetes или приложения GitLab Agent:
Примечание
Чтобы получить токен сервисного аккаунта Kubernetes:
- Создайте сервисный аккаунт.
- Получите токен сервисного аккаунта.
- Сохраните полученный токен — он понадобится для следующих шагов.
Чтобы подключить кластер Yandex Managed Service for Kubernetes к GitLab, создайте GitLab Agent
После установки вы можете подключать кластер Managed Service for Kubernetes к инстансу GitLab.
Подробнее про установку и настройку GitLab Agent читайте в документации GitLab
Настройте сценарий CI
-
Создайте переменные окружения GitLab
:-
На панели слева в GitLab перейдите в раздел Settings и во всплывающем списке выберите пункт CI/CD.
-
Нажмите кнопку Expand напротив пункта Variables.
-
Добавьте переменные окружения в зависимости от способа аутентификации Managed Service for Kubernetes в GitLab:
Токен сервисного аккаунтаGitLab Agent-
KUBE_URL
— адрес мастера Managed Service for Kubernetes. Узнайте его с помощью команды:yc managed-kubernetes cluster get <имя_или_идентификатор_кластера> --format=json \ | jq -r .master.endpoints.external_v4_endpoint
-
KUBE_TOKEN
— токен, который GitLab будет использовать для применения конфигурации. Используйте токен, полученный ранее.
CI_REGISTRY
— адрес созданного ранее реестра в форматеcr.yandex/<идентификатор_реестра>
.CI_REGISTRY_KEY
— ключ, который GitLab будет использовать для доступа к реестру. Скопируйте содержимое файла статического ключаkey.json
для доступа к реестру, полученного ранее.
Для добавления переменной:
- Нажмите кнопку Add variable.
- В появившемся окне в поле Key укажите имя переменной, в поле Value — значение переменной.
- Нажмите кнопку Add variable.
-
-
-
Создайте файл конфигурации сценария CI:
-
Откройте проект
gitlab-test
. -
В строке навигации по репозиторию нажмите кнопку
и в выпадающем меню выберите пункт New file. -
Назовите файл
.gitlab-ci.yml
. Добавьте в него шаги сборки и загрузки Docker-образа, его сканирования на наличие уязвимостей и обновления конфигурации приложения в кластере Managed Service for Kubernetes. Структура файла зависит от способа аутентификации Kubernetes в GitLab:Токен сервисного аккаунтаGitLab Agent.gitlab-ci.yml
stages: - build - test - deploy build_docker_image: stage: build variables: DOCKER_CUSTOM_SUBFOLDER: "" # Specify a custom path (if any) to your folder with docker files. image: name: gcr.io/kaniko-project/executor:debug entrypoint: [""] script: - mkdir -p /kaniko/.docker - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(echo -n "json_key:${CI_REGISTRY_KEY}" | base64 | tr -d '\n' )\"}}}" > /kaniko/.docker/config.json - >- /kaniko/executor --context "${CI_PROJECT_DIR}"/"${DOCKER_CUSTOM_SUBFOLDER}" --dockerfile "${CI_PROJECT_DIR}/"${DOCKER_CUSTOM_SUBFOLDER}"/Dockerfile" --destination "${CI_REGISTRY}/${CI_COMMIT_REF_SLUG}:${CI_COMMIT_SHA}" container_scanning_free_yc: image: name: pindar/jq entrypoint: [""] stage: test artifacts: when: always paths: - gl-container-scanning-report-yc.json script: - export CI_COMMIT_SHA=${CI_COMMIT_SHA} # Install YC CLI. - curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash -s -- -a && cp /root/yandex-cloud/bin/yc /usr/bin/ # Start scanning. - echo "Scanning image $IMAGE_NAME ${CI_REGISTRY}/${CI_COMMIT_REF_SLUG}:${CI_COMMIT_SHA}..." - export IMAGE_ID=$(yc container image list --registry-id (${CI_REGISTRY} | cut -d/ -f2) --format=json | jq -r --arg CI_COMMIT_SHA $CI_COMMIT_SHA '.[] | select(.tags[0]==$CI_COMMIT_SHA) | .id ') # Make a report. - export SCAN_RESULT=$(yc container image scan $IMAGE_ID --format=json) - export CRIT_VULN=$(echo $SCAN_RESULT | jq -r '.vulnerabilities.critical // 0') - export HIGH_VULN=$(echo $SCAN_RESULT | jq -r '.vulnerabilities.high // 0') - export SCAN_ID=$(echo $SCAN_RESULT | jq -r '.id') - echo "Scan results:" - yc container image list-vulnerabilities --scan-result-id="${SCAN_ID}" --format json | jq -r '.[] | select(.severity=="CRITICAL", .severity=="HIGH")' - yc container image list-vulnerabilities --scan-result-id="${SCAN_ID}" --format json | jq -r '.[] | select(.severity=="CRITICAL", .severity=="HIGH")' > gl-container-scanning-report-yc.json # Check the result. - (( SUM = $CRIT_VULN + $HIGH_VULN )) && (( RES = (SUM >= 1) )) && echo $RES && echo "image has $CRIT_VULN critical vulns and $HIGH_VULN high vulns" && exit 1 || echo "image has no high or crit vulns" exit 0 deploy: image: gcr.io/cloud-builders/kubectl:latest stage: deploy script: - kubectl config set-cluster k8s --server="$KUBE_URL" --insecure-skip-tls-verify=true - kubectl config set-credentials admin --token="$KUBE_TOKEN" - kubectl config set-context default --cluster=k8s --user=admin - kubectl config use-context default - sed -i "s,__VERSION__,${CI_REGISTRY}/${CI_COMMIT_REF_SLUG}:${CI_COMMIT_SHA}," k8s.yaml - kubectl apply -f k8s.yaml
.gitlab-ci.yml
stages: - build - test - deploy build_docker_image: stage: build variables: DOCKER_CUSTOM_SUBFOLDER: "" # Specify a custom path (if any) to your folder with docker files. image: name: gcr.io/kaniko-project/executor:debug entrypoint: [""] script: - mkdir -p /kaniko/.docker - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(echo -n "json_key:${CI_REGISTRY_KEY}" | base64 | tr -d '\n' )\"}}}" > /kaniko/.docker/config.json - >- /kaniko/executor --context "${CI_PROJECT_DIR}"/"${DOCKER_CUSTOM_SUBFOLDER}" --dockerfile "${CI_PROJECT_DIR}/"${DOCKER_CUSTOM_SUBFOLDER}"/Dockerfile" --destination "${CI_REGISTRY}/${CI_COMMIT_REF_SLUG}:${CI_COMMIT_SHA}" container_scanning_free_yc: image: name: pindar/jq entrypoint: [""] stage: test artifacts: when: always paths: - gl-container-scanning-report-yc.json script: - export CI_COMMIT_SHA=${CI_COMMIT_SHA} # Install YC CLI. - curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash -s -- -a && cp /root/yandex-cloud/bin/yc /usr/bin/ # Start scanning. - echo "Scanning image $IMAGE_NAME ${CI_REGISTRY}/${CI_COMMIT_REF_SLUG}:${CI_COMMIT_SHA}..." - export IMAGE_ID=$(yc container image list --registry-id (${CI_REGISTRY} | cut -d/ -f2) --format=json | jq -r --arg CI_COMMIT_SHA $CI_COMMIT_SHA '.[] | select(.tags[0]==$CI_COMMIT_SHA) | .id ') # Make a report. - export SCAN_RESULT=$(yc container image scan $IMAGE_ID --format=json) - export CRIT_VULN=$(echo $SCAN_RESULT | jq -r '.vulnerabilities.critical // 0') - export HIGH_VULN=$(echo $SCAN_RESULT | jq -r '.vulnerabilities.high // 0') - export SCAN_ID=$(echo $SCAN_RESULT | jq -r '.id') - echo "Scan results:" - yc container image list-vulnerabilities --scan-result-id="${SCAN_ID}" --format json | jq -r '.[] | select(.severity=="CRITICAL", .severity=="HIGH")' - yc container image list-vulnerabilities --scan-result-id="${SCAN_ID}" --format json | jq -r '.[] | select(.severity=="CRITICAL", .severity=="HIGH")' > gl-container-scanning-report-yc.json # Check the result. - (( SUM = $CRIT_VULN + $HIGH_VULN )) && (( RES = (SUM >= 1) )) && echo $RES && echo "image has $CRIT_VULN critical vulns and $HIGH_VULN high vulns" && exit 1 || echo "image has no high or crit vulns" exit 0 deploy: image: bitnami/kubectl:latest stage: deploy script: - kubectl config use-context ${CI_PROJECT_PATH}:<имя_GitLab_Agent> - cat k8s.yaml | sed -e "s,__VERSION__,${CI_REGISTRY}/${CI_COMMIT_REF_SLUG}:${CI_COMMIT_SHA}," | kubectl apply -f -
Вместо
<имя_GitLab_Agent>
укажите имя агента в Managed Service for GitLab. -
Напишите комментарий к коммиту в поле Commit message:
CI scripts
. -
Нажмите кнопку Commit changes.
В файле
.gitlab-ci.yml
описаны следующие шаги сценария CI:- Сборка Docker-образа с использованием
Dockerfile
и загрузка образа в Container Registry. - Сканирование Docker-образа на наличие уязвимостей в Container Registry.
- Настройка окружения для работы с Kubernetes и применение конфигурации
k8s.yaml
к кластеру Managed Service for Kubernetes. Таким образом приложение развертывается на созданном ранее кластере Managed Service for Kubernetes.
-
Проверьте результат
После сохранения файла конфигурации .gitlab-ci.yml
запустится сценарий сборки. Чтобы проверить результаты его выполнения, на панели слева в проекте gitlab-test
выберите пункт Build, в выпадающем меню выберите пункт Pipelines. Сканирование уязвимостей происходит на втором этапе test
.
Удалите созданные ресурсы
Если созданные ресурсы вам больше не нужны, удалите их:
- Удалите инстанс Managed Service for GitLab или созданную виртуальную машину с образом GitLab.
- Удалите все Docker-образы из реестра Container Registry.
Остальные ресурсы удалите в зависимости от способа их создания:
Чтобы удалить инфраструктуру, созданную с помощью Terraform:
-
В терминале перейдите в директорию с планом инфраструктуры.
-
Удалите конфигурационный файл
k8s-and-registry-for-gitlab.tf
. -
Проверьте корректность файлов конфигурации Terraform с помощью команды:
terraform validate
Если в файлах конфигурации есть ошибки, Terraform на них укажет.
-
Подтвердите изменение ресурсов.
-
Выполните команду для просмотра планируемых изменений:
terraform plan
Если конфигурации ресурсов описаны верно, в терминале отобразится список изменяемых ресурсов и их параметров. Это проверочный этап: ресурсы не будут изменены.
-
Если вас устраивают планируемые изменения, внесите их:
-
Выполните команду:
terraform apply
-
Подтвердите изменение ресурсов.
-
Дождитесь завершения операции.
-
Все ресурсы, которые были описаны в конфигурационном файле
k8s-and-registry-for-gitlab.tf
, будут удалены. -