Trivy + SonarQube: сканирование безопасности в пайплайне GitLab CI

Published: 2026-04-07

Infra-репозиторий запускает два вида Trivy-сканирования: файловой системы (ошибки конфигурации IaC в репозитории) и живого Kubernetes-кластера. Результаты попадают в SonarQube как внешние issue.


Зачем оба сканирования

Тип сканирования Что находит
trivy fs Секреты в git, уязвимые теги образов в HelmRelease values, ошибки в K8s-манифестах
trivy k8s Уязвимости задеплоенных образов, избыточные RBAC-права, ошибки конфигурации в рантайме

Сканирование файловой системы находит проблемы в git. Сканирование кластера проверяет то, что уже работает в production, — например, образы, задеплоенные до публикации CVE.


Структура пайплайна

Стадия безопасности запускается вручную:

yamlstart:security:
  stage: validate:security
  when: manual
  allow_failure: true
  script:
    - echo "Security checks started"

Все джобы безопасности имеют needs: [start:security] — запускаются только после ручного триггера.


Сканирование файловой системы

yamltrivy:scan-repo:
  image: registry.example.com/aquasec/trivy:0.70.0
  needs: [start:security]
  cache:
    key: trivy-db
    paths: [.trivy-cache/]
    policy: pull-push
  script:
    - |
      trivy fs \
        --cache-dir "${TRIVY_CACHE_DIR}" \
        --format sarif \
        --output trivy.sarif \
        --severity HIGH,CRITICAL \
        --exit-code 0 \
        .
  artifacts:
    paths: [trivy.sarif]

--exit-code 0 — джоб не падает при наличии находок. Цель — видимость в SonarQube, а не жёсткая блокировка. --format sarif генерирует SARIF-файл, который SonarQube принимает нативно через sonar.sarifReportPaths.


Сканирование Kubernetes-кластера

yamltrivy:k8s:
  needs: [start:security]
  cache:
    key: trivy-db
    paths: [.trivy-cache/]
    policy: pull-push
  script:
    - |
      trivy k8s \
        --format json \
        --output "trivy-k8s-infra.json" \
        --severity HIGH,CRITICAL \
        --context infra-k8s \
        all
  artifacts:
    paths: ["trivy-k8s-*.json"]

Формат JSON, не SARIF — сканирование trivy k8s не генерирует валидный SARIF напрямую. Конвертация через Python-скрипт.


JSON → формат внешних issue SonarQube

yamltrivy:convert-k8s:
  needs: [trivy:k8s]
  script:
    - |
      for source_file in trivy-k8s-*.json; do
        target_env="${source_file#trivy-k8s-}"
        target_env="${target_env%.json}"
        python3 scripts/trivy-k8s-json-to-sonar.py \
          "$source_file" \
          "trivy-k8s-${target_env}-sonar.json"
      done

trivy-k8s-json-to-sonar.py преобразует находки в формат внешних issue SonarQube:

json{
  "issues": [
    {
      "engineId": "trivy",
      "ruleId": "CVE-2024-1234",
      "severity": "CRITICAL",
      "type": "VULNERABILITY",
      "primaryLocation": {
        "message": "nginx:1.25.3 — известная RCE-уязвимость",
        "filePath": "fluxcd/projects/dev/kustomization/hub/patches/nginx.yaml"
      }
    }
  ]
}

Импорт в SonarQube

yamlsonarqube:trivy:
  image: registry.example.com/sonarsource/sonar-scanner-cli:5.0.1
  needs: [trivy:scan-repo, trivy:convert-k8s]
  variables:
    GIT_STRATEGY: clone
    GIT_DEPTH: "0"        # нужен для git blame
  script:
    - |
      sonar_k8s_reports=$(ls trivy-k8s-*-sonar.json 2>/dev/null | tr '\n' ',' | sed 's/,$//')
      sonar-scanner \
        -Dsonar.host.url=${SONAR_HOST_URL} \
        -Dsonar.token=${SONAR_TOKEN} \
        -Dsonar.projectKey=trivy_infra \
        -Dsonar.sources=. \
        -Dsonar.python.version=3.12 \
        -Dsonar.sarifReportPaths=trivy.sarif \
        ${sonar_k8s_reports:+-Dsonar.externalIssuesReportPaths=${sonar_k8s_reports}}

GIT_DEPTH: "0" — SonarQube использует git blame для привязки находок к коммитам и авторам. Shallow clone ломает blame.

${sonar_k8s_reports:+...} — флаг добавляется только если переменная непустая.


Кэш базы данных Trivy

yamlcache:
  key: trivy-db
  paths: [.trivy-cache/]
  policy: pull-push

База уязвимостей занимает ~300 МБ. Без кэша каждое сканирование скачивает её с GitHub — несколько минут на медленном соединении.


Уведомление

yamlnotify:security:
  when: on_success
  needs: [sonarqube:trivy]

Отправляет сообщение в Telegram и Mattermost с прямой ссылкой на дашборд SonarQube.


Частые проблемы

Trivy: TOOMANYREQUESTS — rate limit GitHub API при скачивании базы. Решение: GitLab CI cache или зеркало Trivy (--db-repository).

SonarQube — нет информации об автореGIT_DEPTH не был "0"; issue остаются без владельца.

trivy k8s завершается по таймауту — добавить --timeout 30m для больших кластеров. Стандартных 5 минут не хватает на 20+ namespace.

SARIF-импорт не работает — убедиться, что версия SonarQube поддерживает SARIF (v9.9+).