Trivy и Docker Scout: сканеры CVE
Hadolint ловит проблемы статикой Dockerfile до сборки. Следующий уровень аудита — уязвимости в собранном образе: какие версии пакетов установлены, какие из них имеют известные CVE (Common Vulnerabilities and Exposures), какие критичные, какие можно проигнорировать.
Стандартные инструменты — Trivy (open-source от Aqua Security) и Docker Scout (встроен в Docker Desktop, проприетарный, частично open).
apt: пакетный менеджер Debian/Ubuntu
Что такое CVE и почему это важно
CVE — это публичная база уязвимостей. У каждой есть ID типа CVE-2024-21626 и оценка severity по CVSS (Common Vulnerability Scoring System, 0-10 баллов). Привет от runc CVE-2024-21626 — позволял из контейнера записать файл на хост через символическую ссылку. CVSS score 8.6, severity HIGH.
Базы CVE ведут разные организации: NVD (NIST), GitHub Advisory, Aqua, RedHat Security Data. Сканеры тянут эти базы и проверяют твой образ.
Каждый пакет (Python lib, apt package, npm module) имеет версию. Сканер собирает inventory (что в образе есть), сверяет с базой CVE (для каждой версии — какие уязвимости известны), выдаёт отчёт.
Trivy: установка и базовый запуск
Trivy — самый популярный open-source сканер. Установка:
# macOS
brew install trivy
# Linux
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin
# Docker
docker run --rm aquasec/trivy image python:3.13-slim
Сканирование образа:
trivy image python:3.13-slim
Первый запуск долгий (минута на скачивание CVE базы ~300 МБ). Последующие — секунды.
Сокращённый вывод:
python:3.13-slim (debian 12.5)
Total: 47 (LOW: 30, MEDIUM: 12, HIGH: 5, CRITICAL: 0)
┌────────────────┬────────────────┬──────────┬─────────────────────┬───────────────┬──────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │
├────────────────┼────────────────┼──────────┼─────────────────────┼───────────────┼──────────────┤
│ libssl3 │ CVE-2024-5535 │ HIGH │ 3.0.13-1~deb12u1 │ 3.0.13-1~deb… │ openssl: ... │
│ libsystemd0 │ CVE-2023-7008 │ MEDIUM │ 252.22-1~deb12u1 │ (no fix) │ systemd: ... │
│ python3.13 │ CVE-2024-8088 │ MEDIUM │ 3.13.0-1 │ 3.13.1 │ python: ... │
└────────────────┴────────────────┴──────────┴─────────────────────┴───────────────┴──────────────┘
Что смотрим:
- Total — сколько CVE найдено по уровням.
- Library / Vulnerability / Severity — что и насколько критично.
- Installed Version — какая версия у тебя в образе.
- Fixed Version — какая версия пакета пофикшена. Если пусто (
no fix) — патча нет.
Severity: LOW / MEDIUM / HIGH / CRITICAL
Эта градация — по CVSS score:
- LOW (CVSS 0.1-3.9). Минорные проблемы, обычно требуют сложных условий для эксплуатации.
- MEDIUM (4.0-6.9). Заметные. Можно копить, патчить плановыми обновлениями.
- HIGH (7.0-8.9). Серьёзные. Должны патчиться в течение спринта.
- CRITICAL (9.0-10.0). RCE, privilege escalation, data leak. Патчатся сегодня.
Для production-образа в DE-команде обычно политика:
- CRITICAL — fail сборки, не деплоим.
- HIGH — fail сборки, но с возможностью override (если нет fix или нет реальной exposure).
- MEDIUM / LOW — отчёт, не блокирует.
В Trivy это настраивается через --severity:
trivy image --severity CRITICAL,HIGH python:3.13-slim
Покажет только HIGH+. В CI:
trivy image --severity CRITICAL --exit-code 1 my-image:latest
# Exit 1 если есть CRITICAL — CI упадёт
Что Trivy ещё сканирует
Помимо образов, Trivy сканирует:
- Файловые системы.
trivy fs .— сканирует текущую директорию (находит уязвимые зависимости вrequirements.txt,package.json,Cargo.toml, и т.д.). - Git-репозитории.
trivy repo https://github.com/user/repo. - IaC (Infrastructure as Code).
trivy config terraform/— находит небезопасные настройки в Terraform, Kubernetes manifest’ах, Dockerfile’ах. - Kubernetes-кластеры.
trivy k8s cluster. - Секреты. Опционально: ищет утечки API-ключей в файлах.
trivy fs --scanners vuln,secret,misconfig .
Это полное сканирование: уязвимости + секреты + misconfig.
Игнорирование CVE
Иногда CVE есть, но не критична для твоего use case. Например, CVE-2024-X в libfoo, но libfoo используется только в Build-стадии multi-stage, в финальный образ не попадает.
Игнорирование через .trivyignore:
CVE-2024-X
CVE-2024-Y
trivy image --ignorefile .trivyignore my-image
Эти CVE не показываются и не фейлят CI.
Важно: каждый ignore должен быть задокументирован. Обычно в .trivyignore пишут с комментарием:
# CVE-2024-X is only in build stage, not in runtime image
# CVE-2024-Y has no fix, mitigated by removing affected feature
CVE-2024-X
CVE-2024-Y
Docker Scout
Docker Desktop с 2023 года имеет встроенный сканер — docker scout. Аналог trivy, но проприетарный и интегрирован в Docker.
docker scout cves python:3.13-slim
Вывод похож на trivy. Преимущества: интеграция с Docker Hub (можно посмотреть scout-отчёт прямо на странице образа), recommendations (предлагает base image с меньшим числом CVE).
docker scout recommendations python:3.13-slim:
Recommended base images:
python:3.13-slim -> 47 CVEs
python:3.13-alpine -> 12 CVEs ← меньше CVE, но musl ломает pandas
cgr.dev/chainguard/python:latest -> 0 CVEs ← distroless с обновлёнными пакетами
Минус Docker Scout: проприетарный, требует логин в Docker Hub для полных функций, в open-source CI неудобен. На дев-машине удобен, в production-pipeline — Trivy чаще.
SBOM (Software Bill of Materials)
SBOM — это «список ингредиентов» образа: все пакеты, их версии, лицензии, происхождение. Это становится требованием регуляторов (EU Cyber Resilience Act, US Executive Order 14028).
Trivy умеет генерировать SBOM:
trivy image --format spdx-json --output sbom.json python:3.13-slim
SBOM в формате SPDX (Linux Foundation standard) или CycloneDX. Содержит:
- Список пакетов с версиями.
- Лицензии (BSD, GPL, MIT, …).
- Зависимости между пакетами.
- Хеши слоёв.
Это полезно для аудита (что у нас в production?) и для compliance (доказать customer’у, что мы знаем, что внутри).
docker buildx тоже умеет генерировать SBOM в момент сборки:
docker buildx build --sbom=true -t my-image .
Как фиксить CVE
Допустим, trivy показал HIGH CVE в libssl3, fixed version 3.0.13-1~deb12u2. Что делать.
Шаг 1: Пересобрать с обновлённой базой. В Dockerfile:
FROM python:3.13-slim
RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
apt-get upgrade -y подтянет последние security-патчи. Пересобрали — trivy image — HIGH’ов меньше.
Шаг 2: Обновить base image. Если в базе python:3.13-slim 2 месяца назад зашитые пакеты — docker pull python:3.13-slim подтянет свежий образ с патчами.
docker pull python:3.13-slim # обновить base
docker build --no-cache -t my-image . # пересобрать с нуля
trivy image my-image # перепроверить
Шаг 3: Сменить base. Если debian-base тянет много CVE — попробовать distroless или chainguard:
FROM cgr.dev/chainguard/python:latest-dev as builder
# ...
FROM cgr.dev/chainguard/python:latest
COPY --from=builder /app /app
ENTRYPOINT ["python", "/app/main.py"]
Chainguard publishes images с минимумом CVE — они сами поддерживают forks основных пакетов с актуальными патчами.
Шаг 4: Если нет fix — mitigation. Когда CVE без fixed version, выбор:
- Признать риск, задокументировать в
.trivyignore. - Удалить уязвимый пакет (если не используется).
- Использовать config-level mitigation (отключить уязвимую фичу).
Интеграция в CI
GitHub Actions для PR:
- name: Build image
run: docker build -t my-image:${{ github.sha }} .
- name: Scan with Trivy
uses: aquasecurity/[email protected]
with:
image-ref: my-image:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1
format: sarif
output: trivy-results.sarif
- name: Upload to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
PR с CRITICAL CVE не пройдёт. SARIF-отчёт публикуется в Security tab репозитория — все CVE видны в UI.
Pattern: nightly re-scan
CVE появляются постоянно. Образ, который сегодня чистый, через неделю может быть с CVE — потому что вышла новая CVE на уже установленный пакет.
Pattern: nightly job в CI пересканирует прод-образы:
on:
schedule:
- cron: '0 4 * * *' # каждый день в 4:00 UTC
jobs:
rescan:
steps:
- run: trivy image --severity CRITICAL prod-image:latest
# Если есть CRITICAL — slack alert + автоматический PR на обновление base
Это закрывает «дрейф безопасности» — между релизами образ ухудшается, и nightly scan ловит это.
Попробуй сам
- Поставь trivy локально (
brew install trivyили docker). - Сканируй типичные базовые образы:
Сравни количество CVE по severity.trivy image python:3.13-slim trivy image python:3.13-alpine trivy image postgres:16 - Сканируй с фильтром:
trivy image --severity CRITICAL,HIGH postgres:16. Сколько HIGH? - Собери Dockerfile с
apt install curlбезapt upgrade. Сканируй — посмотри CVE вlibcurl. Добавьapt upgrade -y, пересобери, сканируй ещё раз. Должно стать меньше. - Сгенерируй SBOM:
trivy image --format spdx-json --output sbom.json python:3.13-slim. Открой файл — увидишь список всех пакетов. - На macOS: попробуй
docker scout cves python:3.13-slimиdocker scout recommendations python:3.13-slim.