ISEC
Quality Check и Quality Gate
Quality Check (QC), или проверка качества, — это процесс, при котором продукты или услуги проверяются на соответствие определенным стандартам или требованиям. Этот процесс важен для обеспечения высокого качества продукта или услуги, что в свою очередь приводит к увеличению удовлетворенности клиентов.
В контексте разработки программного обеспечения, Quality Check часто связан с рядом действий, направленных на обнаружение ошибок или проблем в коде.
Здесь несколько аспектов, которые могут включать в себя Quality Check:
Статический анализ кода. Этот процесс включает в себя анализ кода без его выполнения, чтобы найти потенциальные ошибки, уязвимости и нарушения стиля кодирования. Инструменты статического анализа, такие как SonarQube, ESLint или Checkstyle, могут автоматически проверять код на эти проблемы.
Динамический анализ кода. В отличие от статического анализа, динамический анализ включает в себя исполнение кода и анализ его поведения на различных входных данных. Инструменты динамического анализа, такие как системы автоматизированного тестирования (Junit, TestNG) и инструменты поведенческого тестирования (Cucumber, Behave), используются для этой цели.
Ручное тестирование. В дополнение к автоматическим методам проверки качества, ручное тестирование также важно для проверки функциональности и пользовательского интерфейса. Ручное тестирование обычно выполняется человеком, который переходит через различные функции продукта и проверяет их.
Проверка производительности. Проверка производительности заключается в тестировании приложения или системы на скорость, реакцию и стабильность при различных рабочих нагрузках. Инструменты, такие как Apache JMeter или Gatling, используются для этих целей.
Тестирование безопасности. Это подразумевает поиск уязвимостей, которые могут быть эксплуатированы злоумышленниками. Инструменты, такие как OWASP Zap и Nessus, могут быть использованы для выполнения этих проверок.
Важно понимать, что Quality Check это непрерывный процесс, который должен проводиться на протяжении всего цикла разработки программного обеспечения, начиная с фазы планирования и до фазы поддержки.
Quality Gate (QG) представляет собой проверку качества кода, которая должна быть пройдена перед тем, как изменения в коде могут быть приняты. Это механизм контроля, который обеспечивает соблюдение определенных стандартов и метрик качества в процессе разработки программного обеспечения.
Допустим, вы используете инструменты статического анализа кода такие как SonarQube, тогда Quality Gate может быть настроен для проверки следующих пунктов:
Технический долг. Quality Gate может проверять, не превышает ли технический долг (код или дизайн, которые должны быть улучшены) установленное значение.
Покрытие кода тестами. Quality Gate может проверять показатель покрытия кода тестами, чтобы быть уверенным, что больше установленного процента кода покрыто тестами.
Количество критических и блокирующих ошибок. Quality Gate может проверять количество таких ошибок и предупреждать, если их количество выше приемлемого.
Дублирование кода. Quality Gate может проверять, не превышает ли доля повторяющегося кода определенное значение.
Соответствие стандартам кодирования. Quality Gate может проверять, насколько код соответствует установленным стандартам кодирования.
Ошибки безопасности. Quality Gate может проверить код на наличие уязвимостей, связанных с безопасностью.
Важно понимать, что эти пункты могут варьироваться в зависимости от конкретных требований вашего проекта и сложности технологий. Это могут быть также прочие метрики, например, проверка производительности или соблюдение требований доступности. Цель состоит в том, чтобы наладить процесс постоянного контроля и улучшения качества кода во время его разработки.
Использование Trivy для сканирования образов
Trivy это компактный и простой в использовании сканер безопасности, предназначенный для поиска уязвимостей в контейнерах и образах контейнерах, а также в файлах проекта. Он разработан компанией Aqua Security.
Особенности Trivy:
Широкий спектр поддерживаемых форматов. Trivy может сканировать большинство образов контейнеров, поддерживая такие реестры, как Docker (docker.io), Quay, и Google Container Registry (gcr.io). Кроме того, Trivy поддерживает сканирование Infrastructure as Code (IAC) файлов и файлов зависимостей, таких как package. json (Node.js), Gemfile. lock (Ruby), и pipfile. lock (Python).
Тщательный анализ. Trivy может производить глубокий анализ, ища уязвимости не только в операционной системе, но и в приложениях. Это делает его более полезным, чем некоторые другие инструменты сканирования, которые могут ограничиваться только базовым уровнем.
Простое использование. Trivy упрощает процесс сканирования. Вам просто нужно указать имя образа, и Trivy выполнит остальное. Сканирование можно легко встроить в процесс CI/CD.
Интеграции. Trivy можно легко интегрировать с такими системами как GitHub Actions, CircleCI, GitLab CI и другими, что позволяет заметно упростить включение процессов безопасности в рабочий процесс разработки.
База данных уязвимостей. Trivy использует общедоступные базы данных уязвимостей, такие как NVD (National Vulnerability Database), Red Hat Security Data и другие.
Совместимость и переносимость. Trivy доступен как Docker образ, что упрощает его использование в Docker-ориентированных рабочих процессах.
Важно отметить, что, несмотря на все его возможности, Trivy является одним паззлом в общей стратегии безопасности. Он не заменяет применение политик безопасности, контроля доступа, сетевой безопасности, мониторинга системы и рутинного аудита кода.
Подключаем сканирование Trivy
Основная наша цель сканирования образов Trivy, недопустить push образа с уязвимостями в реестр контейнеров. А наша текущая задача с kaniko сразу собирает и делает push.
Доработка задачи kaniko
Изменим задачу по сборке kaniko.
Введем переменную KANIKO_NOPUSH_FLAG.
Если она установлена, kaniko будет собирать образ в артефакт, но не будет загружать его в harbor. Так же сохраним tar файл образа в артефакт гитлаба, что бы он был доступен на следующей стадии для сканирования.
jobs/kaniko.yml
include:
- local: jobs/git_strategy.yml
variables:
IMAGE_TAG: ""
IMAGE_OUTPUT_DOTENV_KEY: $CI_PROJECT_NAME
IMAGE_OUTPUT_DOTENV_FILE: $CI_PROJECT_NAME
KANIKO_NOPUSH_FLAG: ""
KANIKO_NOPUSH_OPTIONS: ""
.job__kaniko_publish_image:
extends:
- .job__shallow_clone
stage: publish
needs:
- job: Build Package
artifacts: true
interruptible: false
image:
name: $KANIKO_IMAGE
entrypoint: [""]
script:
- b64_auth=$(printf '%s:%s' "$HARBOR_USER" "$HARBOR_PASSWORD" | base64 | tr -d '\n')
- >-
printf '{"auths": {"%s": {"auth": "%s"}}}' "$HARBOR_HOST" "$b64_auth"
>/kaniko/.docker/config.json
- |
if [ "${KANIKO_NOPUSH_FLAG}" == "true" ]; then
export KANIKO_NOPUSH_OPTIONS="--no-push --tar-path ${CI_PROJECT_NAME}.tar"
fi
- >-
/kaniko/executor
--cache
--use-new-run
--skip-unused-stages
--context "$CI_PROJECT_DIR"
--dockerfile "$CI_PROJECT_DIR/Dockerfile"
--destination "$HARBOR_IMAGE:$IMAGE_TAG"
--cache-repo "$HARBOR_IMAGE/cache"
$KANIKO_NOPUSH_OPTIONS
- >-
if [ -n "${IMAGE_OUTPUT_DOTENV_KEY:-}" ]
; then printf '%s=%s' "${IMAGE_OUTPUT_DOTENV_KEY//-/_}_IMAGE" "$HARBOR_IMAGE:$IMAGE_TAG" >"$IMAGE_OUTPUT_DOTENV_FILE"
; fi
artifacts:
paths:
- $IMAGE_OUTPUT_DOTENV_FILE
- ${CI_PROJECT_NAME}.tar
when: on_success
expire_in: 1 day
Скрипт задачи сканирования Trivy
После выполнения сборки проекта с помощью kaniko, необходимо запустить сканирование Trivy. Так как мы делаем Quality Gate из шага сканирования Trivy, нам необходимы некоторые ключи.
exit-on-eol — выдавать ошибки, если ОС образа больше не поддерживается и не получает устранения уязвимостей;
ignore-unfixed — игнорировать уязвимости, которые не имеют версии устранения;
exit-code — выдать ошибку, если найдены уязвимости равные указанному уровню severity.
jobs/trivy.yml
.scan_image_script:
variables:
TRIVY_COMMON_OPTIONS: "--ignore-unfixed --cache-dir /kaniko/cache/.trivycache --exit-on-eol 2 --no-progress"
script:
- |
if [ "$KANIKO_NOPUSH_FLAG" != "true" ]; then
echo 'Для работы trivy сканера нужно чтобы push и build образа проходил раздельно'
echo 'Для включения разделения build и push установите переменную KANIKO_NOPUSH_FLAG: true'
exit 1
fi
- trivy image
--exit-code 1
--input ${CI_PROJECT_NAME}.tar
--severity ${IMAGE_SCAN_SEVERITY_LIST}
--scanners ${IMAGE_SCAN_SCANNERS}
--format ${IMAGE_SCAN_REPORT_FORMAT}
${TRIVY_COMMON_OPTIONS}
Так же в задаче мы используем переменные для настройки сканирования. Их настроим чуть позже, в отдельном файле с переменными.
Push образа
Поскольку мы исправили задачу kaniko и при работе нашего quality gate — мы не загружаем образ заранее, нам необходима задача для загрузки образа в Harbor. Для этого воспользуемся простой утилитой для работы с контейнерами https://github.com/google/go-containerregistry.
Напишем скрипт, загружающий tar образ в Harbor.
jobs/crane_push.yml
.push_image_script:
script:
- 'echo " INFO: Загрузка контейнера ${HARBOR_IMAGE}:${IMAGE_TAG} в реестр..."'
- crane auth login -u ${HARBOR_USER} -p ${HARBOR_PASSWORD} ${HARBOR_HOST}
- crane push ${CI_PROJECT_NAME}.tar ${HARBOR_IMAGE}:${IMAGE_TAG}
Rules для запуска задач сканирования
Наш сканер Trivy будет использоваться как QG для проекта. Так как мы разрабатываем CI шаблон, нам необходимо предусмотреть возможность отключения проверки с помощью переменных. В Pipeline должны появится новые задача, сканирование и загрузка образа. При этом они не должны блокировать проекты, которые не используют на QG.
Для решения этой задачи, введем еще одну переменную ENABLED_SCAN_IMAGE на основе нее напишем правила, запрещающие запуск задач сканирования. Так же запретим запуск сканирования, если kaniko сразу делает push, а так же при задачах по расписанию и merge request.
rules/push_image.yml
include:
- local: rules/main.yml
.push_image_app_rules:
stage: publish
interruptible: true
image:
name: ${WRK_IMG_CRANE}
entrypoint: [""]
rules:
- if: '$ENABLED_SCAN_IMAGE == "false" || $KANIKO_NOPUSH_FLAG != "true"'
when: never
- if: '$CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "merge_request_event"'
when: never
- !reference [.rule__on_default_branch]
- !reference [.rule__on_release_tag]
- when: never
rules/scan_image.yml
include:
- local: rules/main.yml
.scan_image_app_rules:
stage: publish
interruptible: true
image:
name: ${WRK_IMG_TRIVY}
entrypoint: [""]
rules:
- if: '$ENABLED_SCAN_IMAGE == "false" || $KANIKO_NOPUSH_FLAG != "true"'
when: never
- !reference [.rule__on_default_branch]
- !reference [.rule__on_release_tag]
- when: never
Конечно, для задачи сканирования и загрузки образа нам нужен системный образ, на котором мы будет запускать задачи. Если образ trivy можно взять готовый, то go-containerregistryнеобходимо подготовить.
Совместим эти операции:
Dockerfile:
FROM aquasec/trivy:${TRIVY_VERSION} AS base
ADD "https://github.com/google/go-containerregistry/releases/download/v0.19.1/go-containerregistry_Linux_x86_64.tar.gz" go-containerregistry_Linux_x86_64.tar.gz
RUN tar -xf go-containerregistry_Linux_x86_64.tar.gz \
; mv crane gcrane krane /usr/local/bin/ \
; rm -rf go-containerregistry_Linux_x86_64.tar.gz LICENSE README.md
ENTRYPOINT [""]
Полученный образ загружаем в реестр образов и указываем как базовый для задач gitlab runner. Для этого создадим файл с переменными и заполним нужные нам для работы сканера.
Переменные для задачи trivy
Указываем получившийся образ, настраиваем уровень срабатывания сканера (severity). А так же добавляем переменные-флаги по-умолчанию, которые отключают наши задачи.
variables/trivy.yml
variables:
WRK_IMG_TRIVY: registry.devops-teta.ru/materials/ci/images/trivy:0.51.4
WRK_IMG_CRANE: registry.devops-teta.ru/materials/ci/images/trivy:0.51.4
IMAGE_SCAN_SEVERITY_LIST: "LOW,MEDIUM,HIGH,CRITICAL"
IMAGE_SCAN_REPORT_FORMAT: "table"
IMAGE_SCAN_SCANNERS: "vuln,misconfig"
ENABLED_SCAN_IMAGE: 'false'
KANIKO_NOPUSH_FLAG: 'false'
Добавим новый файл в общий список.
variables/main.yml
include:
- variables/default.yml
- variables/vars.yml
- variables/harbor.yml
- variables/stages.yml
- variables/trivy.yml
Подключение задач в шаблон
Осталась последняя часть разработки нашего pipeline. Добавление задач в наш endpoint для сервисов.
Добавим новые задачи Scan image и Push image а так же воспользуемся функционалом optional в needs. Что бы при отсутствии шагов не получать ошибки со сборкой.
pipelines/nextjs_standalone_docker.yml
include:
- local: variables/main.yml
- local: rules/main.yml
- local: jobs/kaniko.yml
- local: jobs/npm.yml
- local: jobs/dotenv_push.yml
- local: /jobs/trivy.yml
- local: /jobs/crane_push.yml
- local: /rules/push_image.yml
- local: /rules/scan_image.yml
Update Cache:
stage: .pre
needs: []
script:
- !reference [.script__npm_clean_install]
extends:
- .job__npm_build
- .npm_cache
- .rule__allow_falure
rules:
- !reference [.rule__enable_lint]
- !reference [.rule__on_merge_requests]
- !reference [.rule__on_default_branch]
- !reference [.rule__on_release_tag]
Lint:
extends:
- .job__npm_build
- .rule__allow_falure
stage: .pre
needs:
- job: Update Cache
artifacts: false
variables:
ESLINT_CODE_QUALITY_REPORT: eslint.codequality.json
script:
- !reference [.script__npm_clean_install]
- npm run lint -- --format gitlab
artifacts:
paths:
- $ESLINT_CODE_QUALITY_REPORT
reports:
codequality: $ESLINT_CODE_QUALITY_REPORT
rules:
- !reference [.rule__enable_lint]
- !reference [.rule__on_merge_requests]
- !reference [.rule__on_default_branch]
Build Package:
extends:
- .job__npm_build
stage: build
needs:
- job: Update Cache
artifacts: false
artifacts:
expire_in: 1h
paths:
- dist
rules:
- !reference [.rule__on_merge_requests]
- !reference [.rule__on_default_branch]
- !reference [.rule__on_release_tag]
Publish Package:
extends:
- .job__kaniko_publish_image
stage: publish
rules:
- !reference [.rule__on_default_branch]
- !reference [.rule__on_release_tag]
scan image:
needs:
- job: Publish Package
optional: true
artifacts: true
extends:
- .scan_image_app_rules
- .scan_image_script
push image:
needs:
- job: Publish Package
optional: true
artifacts: true
- job: scan image
optional: true
artifacts: true
extends:
- .push_image_app_rules
- .push_image_script
Deploy Job:
stage: deploy
extends:
- .job__dotenv_push
needs:
- job: push image
optional: true
artifacts: true
- job: Publish Package
artifacts: true
rules:
- !reference [.rule__on_default_branch]
- !reference [.rule__on_release_tag]
Активация Quality Gate — Trivy в проект
Для активации шагов сканирования в нашем проекте достаточно добавить переменные-флаги.
include:
- project: template
file:
- pipelines/nextjs_standalone_docker.yml
variables:
KANIKO_NOPUSH_FLAG: 'true'
ENABLED_SCAN_IMAGE: 'true'
SEVERITY
В нашем проекте есть уязвимости уровня LOW, MEDIUM и HIGH. Может возникнуть ситуация, когда нас для работы GQ может интересовать только уровень CRITICAL.
Чтобы Trivy не блокировал push образа при отсутствии уровня CRITICAL, можно переопределить переменную:
IMAGE_SCAN_SEVERITY_LIST: "CRITICAL"
Использование SonarQube для анализа кода
SonarQube — это приложение для внедрения статистического анализа кода в процесс разработки программного обеспечения, который поддерживает анализ многих языков программирования.
Под статическим анализом подразумевается, что Sonar создает снепшот первичного анализа кода и в последующем сравнивает состояния между собой.
SonarQube позволяет анализировать код на некачественное оформление, наличие багов, ошибок и возможных проблем безопасности при написании кода нового приложения.
SonarQube позволяет измерять качество программного кода по семи показателям:
Потенциальные ошибки — список багов.
Стиль программирования — например, для Python это проверка отступов внутри кода.
Тесты.
Повторения участков кода — используется DRY концепция.
Комментарии — по соответствию стилистике.
Архитектура и проектирование.
Сложность — в частности, Sonar поддерживает оценку когнитивной сложности кода.
Основной состав SonarQube
Пройдемся по основным вкладкам SonarQube
1) Панель проектов
2) Замечания
3) Правила
У правил есть следующие типы:
- Ошибки
- Уязвимости
- Дефекты
Для правил существует такое понятие как Важность.
Теги
Дата появления правила
И самое интересное — это техдолг, который возникает после срабатывания правила. Общий техдолг считается по формуле: все сработавшие правила умножаются на время их исправления, оцененное Сонаром, и делятся на рабочий день.
4) Quality Gate
Интеграция с GitLab
Конечно для того, что бы использовать SonarQube необходимо сначала его проинтегрировать.
Мы рассмотрим самый простой способ интеграции в ручную.
Для этого вам будет необходимо выполнить следующие шаги:
1. Залогинится в SonarQube и создать проект, связанный с Gitlab.
2. При создании проекта необходимо создать токен внутри Gitlab. Для этого заходим внутрь проекта/группы (в зависимости от планируемого охвата технического токена)
3. После внесения токена можно увидеть все проекты находящиеся в группе к которой привязан токен. Либо проект на который выписывался токен.
4. Далее выбираем способ взаимодействия с SonarQube. В нашем случае это Gitlab CI.
5. Необходимо внести сгенерированные переменные в Gitlab. Вносить необходимо на уровне того проекта который требуется интегрировать. Следует учесть, что переменная SONAR_PROJECT_KEY берется из настроек properties и соответственно нет необходимости использовать ее внутри файла.
6. Вносим в CI файл предложенные параметры
7. После завершения интеграции запускаем CI и проверяем результат.