Сканирование изображений на наличие CVE перед отправкой в репозиторий изображений в рамках CI/CD с использованием Tekton и Buildah
Билдах | Инициатива открытых контейнеров (OCI)| Триви | Драйверы хранилища | КИ/CD | Тектон | Конвейеры OpenShift | Кубернетес | Уязвимости
В Инициативе открытого контейнера ОЧЕНЬ много силы. OCI управляет спецификацией среды выполнения и спецификацией образа. Эта статья будет ссылаться на спецификацию изображения на примере. Вывод спецификации изображения состоит из трех основных разделов. 1. Манифест образа, 2. Файловая система (слои в формате сериализации) и 3. Конфигурация образа. Вам не нужно знать все об этом формате, но это хороший справочник, чтобы понять, как работает сканер Trivy. Итак, начнем.
В этой статье будет использоваться Tekton Task для создания части CI/CD. Buildah, продукт Redhat/IBM, будет использоваться для создания нашего Dockerfile. Перед тем, как начать читать эту статью, рекомендуется изучить и изучить Tekton. Tekton — это облачный инструмент непрерывной интеграции/непрерывного развертывания от Google. Чтобы узнать больше об этом, вы можете посмотреть документацию. Хорошо, давайте создадим нашу задачу.
Шаг 1. Создайте задачу для сборки, сканирования и выталкивания
Создайте файл с именем buildah-task.yml и добавьте следующее:
apiVersion: tekton.dev/v1beta1 kind: Task metadata: namespace: <your-namespace> spec: params: - default: 'false' description: Identifies weather or not to use TLS for the VAPO Quay name: VAPO_QUAY_TLSVERIFY type: string - default: '<your-image-location>' description: Identifies the destination image location and tag name: TAG_IMAGE_NAME type: string - default: Add Image Repository description: Identifies the Base Images name: IMAGE_BASE type: string - default: Add Image Name description: Identifies the Image Name name: IMAGE_NAME type: string - default: 1.0.0 description: The tag for the image name: IMAGE_TAG type: string - default: >- registry.redhat.io/rhel8/buildah@sha256:180c4d9849b6ab0e5465d30d4f3a77765cf0d852ca1cb1efb59d6e8c9f90d467 description: Identifies the Buildah Image name: BUILDAH_IMAGE type: string - default: vfs description: Buildah Storage Driver name: BUILDAH_STORAGE_DRIVER type: string - default: /build/<docker-filename> description: Identifies the location and name of the dockerfile name: DOCKERFILE_NAME type: string - default: . description: Identifies the Docker context root location name: DOCKER_CONTEXT_ROOT type: string - default: oci name: STANDARD_IMAGE_FORMAT type: string - default: /workspace/<repository git pipeline resource name> description: Identifies the location of the source code within the workspace name: WORKING_DIRECTORY type: string - default: 'quay.io/ksummersill2/trivy:0.17.2' description: Identifies the Trivy Image to use for scanning the OCI spec of the image name: TRIVY_IMAGE type: string - default: '0' description: Identifies if Trivy should stop the pipeline name: EXIT_CODE type: string - default: 'os,library' description: Identifies the types of vulnerabilities name: VUL_TYPE type: string - default: 'HIGH,CRITICAL' description: Identifies the severity level for the Trivy scan name: SEVERITY_LVL type: string resources: inputs: - description: Identifies the Pipeline Resource to Clone the Source Code name: source type: git steps: - image: $(params.BUILDAH_IMAGE) name: build-image resources: {} script: > #!/usr/bin/env bash buildah bud --format=$(params.STANDARD_IMAGE_FORMAT) --storage-driver=$(params.BUILDAH_STORAGE_DRIVER) -f $(params.DOCKERFILE_NAME) -t app:1.0.0 . buildah push --format=$(params.STANDARD_IMAGE_FORMAT) --storage-driver=$(params.BUILDAH_STORAGE_DRIVER) app:1.0.0 oci:/workspace/source/image buildah tag --storage-driver=$(params.BUILDAH_STORAGE_DRIVER) app:1.0.0 $(params.TAG_IMAGE_NAME) volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers workingDir: $(params.WORKING_DIRECTORY) - image: 'quay.io/ksummersill2/ubuntu-wget:1.0.0' name: get-latest-cve-findings resources: {} script: > mkdir -p /tekton/home/.cache/trivy/db wget -O /tekton/home/.cache/trivy/db/trivy-offline.db.tgz https://github.com/aquasecurity/trivy-db/releases/latest/download/trivy-offline.db.tgz --no-check-certificate cd /tekton/home/.cache/trivy/db tar xvf trivy-offline.db.tgz - image: $(params.TRIVY_IMAGE) name: initial-oci-scan resources: {} script: > trivy image --skip-update --input /workspace/source/image --exit-code=$(params.EXIT_CODE) --severity=$(params.SEVERITY_LVL) --vuln-type=$(params.VUL_TYPE) --format=table > /workspace/source/cve-report.txt volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers workingDir: $(params.WORKING_DIRECTORY) - image: 'quay.io/ksummersill2/ubuntu-wget:1.0.0' name: send-cve-report-to-nexus3 resources: {} script: > curl -v -u <nexus-username>:<nexus-password> --upload-file /workspace/source/cve-report.txt <nexus-repo-location> - image: $(params.BUILDAH_IMAGE) name: push-image-to-vapo-quay-dev resources: {} script: > buildah push --storage-driver=$(params.BUILDAH_STORAGE_DRIVER) --tls-verify=$(params.VAPO_QUAY_TLSVERIFY) $(params.TAG_IMAGE_NAME) volumeMounts: - mountPath: /var/lib/containers name: varlibcontainers workingDir: $(params.WORKING_DIRECTORY) volumes: - emptyDir: {} name: varlibcontainers
Здесь МНОГО всего происходит, так что давайте разберемся.
На самом деле эта задача состоит из 5 шагов. Это 1. Создание образа, 2. Получение последних результатов CVE, 3. Начальное сканирование OCI, 4. Отправка отчета CVE в Nexus, 5. Отправка образа в Quay. Итак, давайте разберем каждый.
1. Создайте образ
Так что этот конкретный шаг, конечно, создает образ, но если вы присмотритесь, он на самом деле делает больше.
buildah bud --format=$(params.STANDARD_IMAGE_FORMAT) --storage-driver=$(params.BUILDAH_STORAGE_DRIVER) -f $(params.DOCKERFILE_NAME) -t app:1.0.0 .
В этой части шагов используется драйвер VFS с форматом OCI для создания образа с помощью команды «bud». Bud — это команда в Buildah, которая позволяет создавать образ из «Dockerfile». Затем изображение помечается как «приложение: 1.0.0».
Хорошо, создайте, у нас есть изображение, но теперь его нужно преобразовать в определенный формат.
buildah push --format=$(params.STANDARD_IMAGE_FORMAT) --storage-driver=$(params.BUILDAH_STORAGE_DRIVER) app:1.0.0 oci:/workspace/source/image
Следующий шаг делает именно это. В следующей части используется Buildah push для передачи изображения в формате спецификации изображения OCI в конкретный каталог с именем /workspace/source/image. Да! это правильно, это помещает весь образ приложения: 1.0.0 в каталог в рабочей области Tekton. Просто подожди, станет лучше.
Таким образом, чтобы подготовить изображение к последующей отправке, оно помечено.
buildah tag --storage-driver=$(params.BUILDAH_STORAGE_DRIVER) app:1.0.0 $(params.TAG_IMAGE_NAME)
Таким образом, этот шаг просто помечает изображение. Ничего особенного. Обратите внимание: вы должны указать драйвер хранилища, иначе образ НЕ будет найден. Давайте двигаться дальше.
2. Получите последние результаты CVE
В следующей части используется подход AirGap с Trivy. Если вы хотите узнать больше о подходе AirGap, прочитайте мою статью здесь. https://ksummersill.medium.com/setting-up-trivy-for-airgap-approach-within-ci-cd-3d429da21de4
mkdir -p /tekton/home/.cache/trivy/db wget -O /tekton/home/.cache/trivy/db/trivy-offline.db.tgz https://github.com/aquasecurity/trivy-db/releases/latest/download/trivy-offline.db.tgz --no-check-certificate cd /tekton/home/.cache/trivy/db tar xvf trivy-offline.db.tgz
Этот шаг в основном создает каталог с именем /tekton/home/.cache/trivy/db. Сюда будет помещена автономная база данных. Затем автономная база данных извлекается из официального репозитория Trivy Github, а затем помещается в созданный каталог. Затем файл Tar извлекается из этого каталога для использования на следующем шаге.
3. Начальное сканирование OCI
Так вот где происходит вся магия. Большинство сканирований CVE выполняются после того, как образ находится в репозитории. Но подождите, что, если вы можете сканировать образ на наличие уязвимостей перед отправкой. Я имею в виду, что мы можем сделать это с исходным кодом, почему бы и нет с изображениями?
trivy image --skip-update --input /workspace/source/image --exit-code=$(params.EXIT_CODE) --severity=$(params.SEVERITY_LVL) --vuln-type=$(params.VUL_TYPE) --format=table > /workspace/source/cve-report.txt
Что ж, приведенный выше код делает именно это. Помните, что мы используем команду buildah push для отправки изображения в формате OCI с использованием спецификации изображения OCI в /workspace/source/image. Ну, угадайте что! На самом деле вы можете отсканировать это с помощью Trivy, используя:
trivy image --skip-update --input <location of image>
Обратите внимание, что вы должны использовать skip-update, иначе trivy попытается использовать обычный подход для извлечения базы данных. Единственная причина, по которой использовался подход AirGap, заключалась в ограничениях среды, в которой я работал. Итак, теперь поле ввода является ключом для определения папки, в которой находится изображение.
Теперь «код выхода» позволяет определить, обнаружены ли «результаты» для отказа конвейера CI/CD или для продвижения вперед. Это делается с помощью 0 для продвижения вперед или 1 для продолжения. Конечно, у вас также есть уровни серьезности и типы уязвимостей, из которых можно выбирать, как проводится эта «проверка ворот». Уровни серьезности: критический, высокий, средний и низкий. Принимая во внимание, что типы уязвимостей — это операционные системы (ОС) или библиотеки. Обычно это происходит, если у вас есть приложение, загруженное в образ, или вы хотите проверить наличие зависимостей, отличных от операционной системы. Триви отлично справляется с этой задачей.
4. Отправка результатов сканирования в Nexus
Теперь в этом следующем шаге нет ничего особенного, за исключением того, что вы можете отправить результаты своей команде безопасности в централизованную область.
curl -v -u <nexus-username>:<nexus-password> --upload-file /workspace/source/cve-report.txt <nexus-repo-location>
5. Наконец, нажмите на изображение
После того, как вы проверили все необходимые проверки ворот, теперь вы можете отправить образ в репозиторий образов.
buildah push --storage-driver=$(params.BUILDAH_STORAGE_DRIVER) --tls-verify=$(params.VAPO_QUAY_TLSVERIFY) $(params.TAG_IMAGE_NAME)
Ну, это почти все. Конечно, если вы хотите запустить эту задачу, это будет сделано с помощью запуска задачи или запуска конвейера. Если вы хотите, чтобы я продолжил эту статью о том, как реализовать, то, пожалуйста, напишите мне, и я посмотрю, как продолжить реализацию этого. Здорово! Надеюсь, это помогло любому, кто пытается понять, как сканировать изображение перед отправкой с использованием спецификации изображения, совместимой с OCI.