сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Безопасность инфраструктуры

Цепочка поставки: сканеры, подписи, SBOM

Цепочка поставки

Посчитал, что вы на самом деле берёте с полки. В чистом образе python установлен 1 пакет. Одна команда установки самого обычного веб-фреймворка - и пакетов становится 8: семь чужих библиотек приехали как зависимости, и ни одну из них никто не выбирал. А сам базовый образ debian приносит 88 системных пакетов и 5242 файла ещё до вашего кода.

Стержень: в проде работает весь конус зависимостей вместе с вашим кодом, и управлять надо им целиком - версиями, обновлением и проверкой.

// Формулировки: «как контролировать зависимости?», «зачем сканировать образы?», «что такое состав артефакта?»

Что именно приезжает в прод

Три источника, и все три надо считать. Первый - базовый образ, тот, с которого начинается сборка: у меня debian принёс 88 пакетов и 5242 файла, из них 8 программ, работающих с правами root. Тот же alpine - 14 пакетов и 89 файлов, программ с правами root ноль. Разница в поверхности атаки на два порядка, и выбирается она одной строкой в файле сборки.

Второй - зависимости приложения: один установленный фреймворк принёс семь транзитивных зависимостей. Каждая из них имеет своего автора, свою историю обновлений и свой шанс однажды оказаться заброшенной или перехваченной.

// Третий - инструменты сборки, которые не должны доезжать до прода вовсе. Компилятор, менеджер пакетов, отладочные утилиты в готовом образе - это и лишний вес, и готовый инструментарий для того, кто попал внутрь. Лечится многостадийной сборкой: собрали в тяжёлой стадии, скопировали результат в лёгкую.

транзитивная зависимость
библиотека, которую притащила другая библиотека; её никто не выбирал
поверхность атаки
всё, что есть в образе и может быть использовано попавшим внутрь

Закрепление версий и обновление

Плавающая версия означает, что сегодняшняя сборка и завтрашняя - разные, а разобраться, что именно изменилось, невозможно. Поэтому версии закрепляют: приложения - файлом со всеми версиями зависимостей, включая транзитивные; базовый образ - отпечатком содержимого, а не тегом. Тег можно перевесить на другой образ, я это показывал; отпечаток нельзя.

Но закрепление создаёт вторую проблему: закреплённое устаревает и копит известные уязвимости. Значит, нужен и обратный процесс - регулярное обновление закреплений, желательно автоматическое, с прогоном тестов. Схема, которая работает: робот раз в неделю поднимает версии и открывает изменение, тесты проходят, человек смотрит и вливает.

// Ошибка на этом месте типовая: закрепили и забыли. Через год оказывается, что обновление невозможно, потому что версии разъехались на несколько крупных выпусков, в которых менялась совместимость, и всё ломается разом. Обновляться понемногу и часто дешевле, чем один раз и целиком.

закрепление версий
точные версии всех зависимостей, включая транзитивные
обновление закреплений
регулярный автоматический подъём версий с прогоном тестов

Сканирование, состав и подпись

Сканер сверяет список того, что стоит в образе, с базой известных уязвимостей. Ставят его в двух местах: в сборку, чтобы не выпускать новое с известными дырами, и по расписанию на то, что уже работает, - потому что уязвимость находят ПОСЛЕ выпуска, и вчера чистый образ сегодня уже нет.

Состав артефакта - это машиночитаемый список всего, что вошло в сборку, с версиями. Нужен он ровно для одного вопроса, который однажды прилетает: «в какой из наших сборок есть эта библиотека нужной версии». Без списка ответ ищется руками неделю, со списком - запросом за минуту.

// Подпись артефакта закрывает третью дыру: она подтверждает, что образ собран вашим сборочным конвейером, а не подсунут по дороге. Проверку подписи ставят на стороне, которая запускает: не подписан нашим ключом - не запускаем. Это же отсекает и «случайно выкатили образ, собранный на ноутбуке».

сканирование
сверка состава образа с базой известных уязвимостей
состав артефакта
машиночитаемый список всего, что вошло в сборку, с версиями
подпись артефакта
подтверждение, что образ собран вашим конвейером

Как отвечать: «Как контролировать то, что приезжает в прод вместе с кодом?»

Считать всё, что приезжает, а приезжает много. Я специально мерил: чистый образ python содержит один пакет, а после установки одного обычного фреймворка их восемь - семь транзитивных зависимостей никто не выбирал. Базовый образ debian сверх этого приносит 88 системных пакетов и больше пяти тысяч файлов, включая восемь программ, работающих с правами root; у alpine это 14 пакетов, 89 файлов и ноль таких программ. То есть выбор базы меняет поверхность атаки на два порядка одной строкой. Дальше по порядку: закрепляю версии приложения файлом со всеми транзитивными зависимостями, а базовый образ - отпечатком содержимого вместо тега, потому что тег можно перевесить. Ставлю автоматическое обновление закреплений раз в неделю с прогоном тестов, иначе через год обновиться будет невозможно. И держу сканирование в двух местах: в сборке и по расписанию на то, что уже работает, потому что уязвимость находят после выпуска.

Ответ показывает масштаб проблемы числами и даёт четыре конкретных механизма. Замечание про сканирование уже работающего отличает практика от того, кто поставил сканер в сборку и успокоился.

На чём валятся

  • Берут полновесный базовый образ по привычке и тащат в прод тысячи лишних файлов.
  • Закрепляют версии и не обновляют их: через год обновление ломает всё разом.
  • Ссылаются на базовый образ тегом, который можно перевесить, а не отпечатком.
  • Сканируют только при сборке и не сканируют то, что уже работает.
  • Оставляют в готовом образе компилятор и пакетный менеджер.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 6, остальные разбираются в тренажёре.

  1. #dvo_supply_chain1 / 5
    Зачем сканировать собранные образы, если код прошёл ревью?
    A)В базовом слое и зависимостях есть уязвимости
    B)Сканер проверяет корректность Dockerfile и порядок инструкций
    C)Так измеряется размер образа и находятся лишние слои
    D)Сканер подтверждает, что образ собран из нужной ветки
    показать ответ и разбор
    +A)В базовом слое и зависимостях есть уязвимости

    // разбор: Свой код — малая часть образа: остальное это базовый дистрибутив, системные библиотеки и десятки транзитивных зависимостей, где регулярно находят уязвимости. Сканер сверяет установленные пакеты с базами известных проблем и показывает, что чинится обновлением, а что придётся закрывать иначе. Проверять нужно и на сборке, и периодически в реестре: уязвимость может стать известной уже после публикации образа.

  2. #dvo_supply_chain2 / 5
    Сканер показывает критическую уязвимость в системной библиотеке базового образа. Приложение её не использует. Что делать?
    A)Добавить исключение в сканер: раз не используется, риска нет
    B)Заменить базовый образ на другой дистрибутив
    C)Пересобрать на свежем базовом образе и зафиксировать новый дайджест
    D)Удалить библиотеку из образа отдельной инструкцией в конце сборки
    показать ответ и разбор
    +C)Пересобрать на свежем базовом образе и зафиксировать новый дайджест

    // разбор: Обычный путь — обновление: свежий базовый образ приносит исправленный пакет, сборка перезапускается, дайджест пинится заново. «Мы это не вызываем» — слабый аргумент: зависимость может дёрнуть библиотеку неявно, а проверять это на каждом релизе никто не будет. Исключения оформляют осознанно и со сроком, когда обновления ещё нет, а сервис нужен в проде.

  3. #dvo_supply_chain3 / 5
    Как убедиться, что в прод едет именно тот образ, который собрал наш пайплайн?
    A)Проверять имя и тег образа при выкатке
    B)Ограничить доступ к реестру только адресами кластера
    C)Хранить историю сборок и сверять её вручную при инцидентах
    D)Подписывать в пайплайне, проверять при допуске
    показать ответ и разбор
    +D)Подписывать в пайплайне, проверять при допуске

    // разбор: Тег переставляется, а доступ к реестру можно получить. Подпись привязывает образ к конкретной сборке: пайплайн подписывает дайджест, а контроллер допуска в кластере пропускает только подписанные доверенным ключом. Рядом обычно держат состав образа (SBOM) — по нему за минуту видно, задет ли прод очередной уязвимостью в библиотеке, без пересканирования всего парка.

  4. #dvo_supply_chain4 / 5
    Сборка тянет зависимости по диапазону версий (^1.2). Чем это рискованно для supply chain?
    A)Диапазон версий замедляет установку зависимостей
    B)Проблема лишь в возможной несовместимости API
    C)Подтянется чужая версия; спасает lock-файл
    D)Диапазон мешает кэшировать зависимости в CI
    показать ответ и разбор
    +C)Подтянется чужая версия; спасает lock-файл

    // разбор: Плавающий диапазон версий (^1.2, latest) означает, что сборка может подтянуть новую версию зависимости — в том числе скомпрометированную (взломанный мейнтейнер, вредоносный релиз). Защита: lock-файл, фиксирующий точные версии и криптографические хеши (package-lock, poetry.lock, go.sum) — сборка воспроизводима, а подмена содержимого пакета ломает проверку хеша. Обновления зависимостей делают осознанно и ревьюят.

  5. #dvo_supply_chain5 / 5
    Вышла громкая уязвимость в популярной библиотеке. Как быстро понять, какие ваши сборки затронуты?
    A)Пересобрать вообще всё на всякий случай
    B)По SBOM: где именно лежит эта библиотека и версия
    C)Дождаться, пока сканер образов сам поднимет тревогу
    D)Спросить разработчиков по памяти, кто её использовал
    показать ответ и разбор
    +B)По SBOM: где именно лежит эта библиотека и версия

    // разбор: SBOM (Software Bill of Materials) — машиночитаемый список всех компонентов и версий в артефакте, включая транзитивные зависимости. Когда выходит новая CVE, по SBOM всех сборок за минуты отвечают на вопрос «затронуты ли мы и где именно» — вместо ручного перебора и слепой пересборки. Поэтому SBOM генерируют в пайплайне и хранят вместе с артефактом; на нём же строят непрерывное сопоставление с базами уязвимостей.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.