Сканирование уязвимостей образов
Как находить известные уязвимости в собранных образах и снижать риск ещё до деплоя.
CVE (Common Vulnerabilities and Exposures) — публичный идентификатор известной уязвимости, например
CVE-2023-1234. Сканер образа сверяет пакеты внутри с базой CVE и сообщает о совпадениях.
Образ кажется «вашим кодом», но на 90% состоит из чужого: базовый дистрибутив (Debian, Alpine), системные библиотеки (openssl, zlib), интерпретатор или рантайм, зависимости приложения. В любом из этих компонентов со временем находят уязвимости. Образ, собранный полгода назад, мог быть чистым, а сегодня в его версии openssl уже есть критический CVE. Сканирование — это автоматическая проверка «что из известного плохого приехало вместе с образом». Этот урок — про оборону: как находить и чинить, а не эксплуатировать.
Зачем это на практике
Уязвимый образ в проде — это потенциальная точка входа для атакующего и провал любого аудита безопасности. Сканер встраивают в CI, чтобы ловить проблему до релиза: собрали образ → просканировали → если есть критичные CVE, сборка падает и образ не уезжает в прод. Дешевле обновить базовый образ на этапе сборки, чем разбирать инцидент.
Откуда в образе берутся CVE
Источников несколько, и полезно различать их, потому что чинятся они по-разному:
- Базовый образ.
FROM python:3.11тянет целый Debian с десятками системных пакетов. Их уязвимости становятся вашими. - Системные библиотеки.
libssl,libc,curl— низкоуровневые компоненты, в которых регулярно находят дыры. - Зависимости приложения. Пакеты из
requirements.txt/package.json— у них свои CVE. - Старость образа. CVE «появляется» не в образе, а в базе данных уязвимостей. Образ не менялся, но мир узнал о проблеме — и теперь сканер её видит.
Инструменты: trivy и docker scout
Trivy (Aqua Security) — популярный open-source сканер. Принимает имя образа, скачивает актуальную базу CVE и выдаёт отчёт.
# Просканировать образ
trivy image ghcr.io/acme/api:1.4.2
# Только критичные и высокие, остальное скрыть
trivy image --severity CRITICAL,HIGH ghcr.io/acme/api:1.4.2
# Падать с ненулевым кодом, если есть CRITICAL — удобно для CI
trivy image --exit-code 1 --severity CRITICAL ghcr.io/acme/api:1.4.2
docker scout — встроенный в Docker CLI инструмент. Удобен «из коробки» и хорошо показывает, какие CVE уйдут, если обновить базовый образ.
# Сводка по уязвимостям образа
docker scout cves ghcr.io/acme/api:1.4.2
# Рекомендации: на какой базовый образ перейти
docker scout recommendations ghcr.io/acme/api:1.4.2
Как читать отчёт
Отчёт сканера — это таблица: для каждой уязвимости указаны пакет, установленная версия, версия с фиксом, идентификатор CVE и уровень опасности (severity). Уровни обычно: CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN.
Package Installed Fixed In Vulnerability Severity
openssl 3.0.11 3.0.13 CVE-2024-XXXX CRITICAL
libcurl 8.4.0 8.5.0 CVE-2023-YYYY HIGH
zlib 1.2.13 (none) CVE-2023-ZZZZ MEDIUM
Ключевая колонка — Fixed In. Если там есть версия — уязвимость исправляется обновлением пакета или базового образа. Если стоит (none) — фикса пока нет, и решать придётся иначе (принять риск с обоснованием, временно подавить конкретный CVE, поискать другой базовый образ). Не пытайтесь закрыть «все 200 LOW» сразу — приоритет всегда у CRITICAL и HIGH с доступным фиксом.
Главное лекарство — обновить базовый образ
Большая часть системных CVE лечится одним движением: взять свежий базовый образ. Сопровождающие официальных образов регулярно пересобирают их с пропатченными пакетами.
# Было — старый и, возможно, дырявый
FROM python:3.11.4-slim
# Стало — свежий патч той же ветки
FROM python:3.11-slim
Пересоберите образ — и десятки системных CVE исчезнут, потому что внутри уже обновлённые openssl, libc и прочее. Дополнительно помогает выбор минимального базового образа: -slim или alpine содержат меньше пакетов, а значит — меньше поверхности для уязвимостей. Регулярная пересборка (хотя бы по расписанию в CI) удерживает образ «свежим» сама по себе.
SBOM: опись содержимого
SBOM (Software Bill of Materials) — машиночитаемый список всего, что входит в образ: каждый пакет и его версия. Это как состав на упаковке продукта. Зачем он нужен оборонительно: когда завтра объявят новый громкий CVE в какой-нибудь библиотеке, вам не придётся пересканировать всё — достаточно поискать пакет в сохранённых SBOM и сразу понять, какие образы затронуты.
# Сгенерировать SBOM образа в стандартном формате
docker scout sbom ghcr.io/acme/api:1.4.2
# trivy тоже умеет SBOM (например, формат CycloneDX)
trivy image --format cyclonedx --output sbom.json ghcr.io/acme/api:1.4.2
Как это работает под капотом
Сканер не «угадывает» уязвимости и не запускает код. Он инвентаризует образ: разбирает слои, читает метаданные пакетных менеджеров (база dpkg/apk, файлы requirements.txt, lock-файлы) и составляет список «пакет → версия». Затем сверяет этот список с локально скачанной базой уязвимостей (агрегат из NVD, дистрибутивных трекеров и др.): для каждого пакета известно, в каких версиях есть какие CVE и где их починили. Совпадение версии с уязвимым диапазоном попадает в отчёт. Поэтому критично держать базу свежей — сканер видит ровно то, что в ней есть. Тот же образ, просканированный сегодня и через неделю, может дать разный результат: добавились новые CVE.
Частые ошибки
- Не сканировать вообще. «У нас же свой код» — но 90% образа чужое. Без сканера вы про дыры просто не знаете.
- Гнаться за нулём. Попытка закрыть все LOW/MEDIUM выматывает команду. Сфокусируйтесь на CRITICAL/HIGH с фиксом.
- Сканировать раз и забыть. CVE появляются постоянно. Нужна регулярная пересборка и повторное сканирование, а не разовая проверка.
- Игнорировать колонку Fixed In. Половина проблем лечится сменой базового образа — а команда вручную правит зависимости.
- Тяжёлый базовый образ. Полный
ubuntuвместо-slim/alpine= лишние пакеты = лишние CVE.
Итоги
- Образ почти весь состоит из чужого кода; уязвимости приезжают с базовым образом, библиотеками и зависимостями.
- CVE «появляется» при пополнении базы данных — старый неизменный образ со временем становится «уязвимым».
- Сканируйте через
trivyилиdocker scout, встраивайте проверку в CI с падением на CRITICAL. - Главное лекарство — обновить базовый образ и пересобрать; смотрите колонку Fixed In.
- SBOM — опись содержимого; помогает быстро находить затронутые образы при новом CVE.