open atlas
↑ К треку
Docker: контейнеры как система DOCK · 07 · 02

Сканирование образов: почему жирная база везёт бэклог CVE и что Trivy и Grype реально измеряют

Жирная база (ubuntu/node) несёт 300+ OS-пакетов и длинный хвост CVE, что ты не вызываешь; distroless роняет это до единиц. Trivy и Grype сопоставляют версии пакетов с базами уязвимостей — находят известные CVE в твоём SBOM, не эксплуатируемость, так что рычаг — ужать базу.

DOCK Senior ◷ 15 min
Уровень
ОсновыJuniorMiddleSenior

Разбор взлома назвал CVE, о которой никто в команде раньше не слышал: уязвимость удалённого исполнения кода в логирующей библиотеке — Log4Shell — которую приложение даже не использовало напрямую. Её затянуло транзитивно, запекло в образ node:18 месяцами раньше, и она ушла в прод нетронутой, потому что никто не сканировал образ после выбора базы. Соль пришла, когда инженер наконец прогнал сканер по тому же образу: он отчитался о более чем трёхстах пакетах и десятках high- и critical-CVE, большинство — в OS-библиотеках, что сервис никогда не вызывал: тут curl, там perl-модуль, библиотека шрифтов, существовавшая лишь потому, что база была почти полным Debian-userland. Уязвимый логирующий jar был одной иголкой в стоге сена, что команда таскала без причины. Фиксом была не лихорадочная охота на CVE; это была пересборка на distroless-базе, что несла рантайм и почти ничего больше, что уронило число пакетов из трёхсот в единицы и утянуло бэклог CVE за собой.

Что сканер реально делает — и чего нет

Прежде чем действовать по результатам скана, нужно понять, что инструмент вообще измеряет — потому что путаница между «числом найденных CVE» и «реальным риском для сервиса» ведёт либо к панике, либо к ложной уверенности.

Сканер уязвимостей контейнеров вроде Trivy или Grype в своей сути — это join. Сначала он строит Software Bill of Materials (SBOM): обходит слои образа и перечисляет каждый установленный артефакт, что может опознать — OS-пакеты из базы dpkg/rpm/apk плюс языковые зависимости из lock-файлов вроде package-lock.json, go.sum или Gemfile.lock — каждый с именем и точной версией. Затем он сопоставляет эти пары имя+версия с базой уязвимостей (NVD плюс distro-специфичные фиды вроде security-трекеров Debian и Alpine, GitHub-advisories и так далее) и выдаёт каждую CVE, чей диапазон затронутых версий включает то, что ты поставил. Это весь механизм, и его пределы вытекают прямо из него: сканер находит известные уязвимости в объявленных версиях. Он не знает, достигается ли уязвимый путь кода в рантайме, бэкпортнул ли фикс твой distro (частый источник ложных срабатываний, когда upstream-строка версии выглядит уязвимой, но distro пропатчил), и уязвимо ли вообще что-либо не из распознанного манифеста пакетов — руками curl-нутый бинарь, вендоренный .so. Сканирование измеряет твою поверхность экспозиции как объявлено, не твою эксплуатируемость.

Вот почему результат доминирует твоей базой, а не твоим кодом. Поставь на ubuntu или node — и сканер перечислит весь base-userland: типичная жирная база несёт 300-с-лишним OS-пакетов, и в любой момент срез из них имеет нерешённые high- или critical-CVE — не потому, что твой сервис небезопасен, а потому, что ты таскаешь почти полный Linux-дистрибутив, чтобы гонять один бинарь. Большинство этих пакетов — шеллы, пакетные менеджеры, библиотеки сжатия, интерпретатор-другой — никогда не вызываются твоим приложением. Это чистая поверхность атаки и чистый бэклог CVE. Самый эффективный сократитель результатов сканирования, стало быть, не патчинг, а удаление: distroless-база (лишь glibc, CA-сертификаты и твой рантайм, без шелла и пакетного менеджера) режет число пакетов до единиц, и число CVE схлопывается с ним, потому что попросту почти нечего сопоставлять с базой.

Викторина

Код сервиса команды имеет ноль известных уязвимостей, но Trivy отчитывается о 40 high/critical-CVE по собранному образу. Откуда эти CVE почти наверняка, и какой фикс с наибольшим рычагом?

Почему это работает

Почему просто не гейтить пайплайн на нуле CVE и покончить? Потому что жёсткий гейт нуля critical против жирной базы — невыполнимый театр: у базы всегда будет какая-то свежераскрытая CVE без фикса, так что гейт либо блокирует каждый деплой, либо обходится до бесполезности. Сеньорская политика разделяет две вещи: ужать базу, чтобы число было естественно низким (единицы, не сотни), и гейтить на исправимых critical с доступной фикс-версией, а не на сыром числе. CVE без upstream-фикса — это пункт трекинга, не блокер деплоя; critical с доступным однострочным бампом базы — блокер. Считать дёшево; решать, что считается, — работа.

Чтение severity, исправимости и SBOM, что ты хранишь

Сканеры раскладывают находки по severity (CRITICAL/HIGH/MEDIUM/LOW, из CVSS) и, критично, по исправимости — есть ли фикс-версия. Число, что должно двигать действие, — это «critical и high с доступным фиксом», ведь это те, что бамп базы или обновление зависимости реально решает; остальные трекаются, не гонятся. Не менее важен артефакт, что скан попутно производит: SBOM ценен сам по себе. Сохранённый рядом с образом (в форматах вроде CycloneDX или SPDX, часто лишь десятки-сотни килобайт JSON), он позволяет ответить «затронуты ли мы завтрашней CVE?», запросив инвентарь, что ты уже записал, вместо пересканирования с нуля под давлением инцидента — а это ровно вопрос, что Log4Shell заставил каждую команду решить за ночь. Сканирование, стало быть, — два вывода: приоритизированный, осведомлённый об исправимости список находок, что двигает петлю ужми-базу-затем-патч, и долговечный SBOM, что превращает следующий zero-day из лихорадочных раскопок образа в запрос к базе.

Викторина

Какой самый полезный способ гейтить CI-пайплайн на результатах сканера для образа, собранного на минимальной базе?

Вспомните перед уходом
  1. 01
    Опиши, что именно делает сканер вроде Trivy или Grype, и три вещи, что он не может сказать.
  2. 02
    Почему базовый образ доминирует результаты сканирования, и каков способ их сократить с наибольшим рычагом?
Итог

Сканер контейнеров вроде Trivy или Grype фундаментально — это join: он строит Software Bill of Materials, перечисляя каждый опознаваемый установленный артефакт в слоях образа — OS-пакеты из dpkg/rpm/apk и языковые зависимости из lock-файлов, каждый с именем и точной версией — затем сопоставляет эти пары с базами уязвимостей (NVD плюс distro- и экосистемные фиды) и выдаёт каждую CVE, чей затронутый диапазон покрывает поставленную версию. Его пределы выпадают прямо из механизма: он отчитывается об известных CVE в объявленных версиях, не о том, достижим ли уязвимый путь, не всегда о том, бэкпортнул ли distro фикс (частый источник ложняков), и не о чём-либо вне распознанного манифеста. Вот почему результаты доминируются базой, не твоим кодом: жирная база вроде ubuntu или node несёт 300-с-лишним OS-пакетов и стабильный хвост high/critical-CVE, большинство — в библиотеках, что сервис никогда не вызывает, так что чистое приложение всё равно наследует сотни находок. Самый рычажный сократитель — удаление, не патчинг: distroless-база с лишь glibc, CA-сертификатами и рантаймом роняет число пакетов до единиц и число CVE с ним. Гейть пайплайн на исправимых critical и high, а не на сыром числе, считай неисправимые CVE пунктами трекинга и храни SBOM (CycloneDX или SPDX, десятки-сотни КБ), чтобы следующий zero-day — вопрос Log4Shell, что каждая команда решала за ночь — стал запросом к инвентарю, что ты уже записал, вместо лихорадочного пересканирования под давлением инцидента. Теперь, когда откроешь отчёт сканера с сотнями находок, первый вопрос будет не «какую CVE патчить?», а «на какой базе я собираю и можно ли перейти на distroless прямо сейчас?».

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 5 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.