Log4j 2.17.0 и SBOM: три дня против двух часов
Apache выпускает Log4j 2.17.0 как финальный фикс - 2.15.0 и 2.16.0 неполны. Log4Shell доказал, что инвентарь компонентов - это не опция.
Apache выпускает Log4j 2.17.0 как финальный фикс - 2.15.0 (CVE-2021-45046) и 2.16.0 (CVE-2021-45105) содержат неполные исправления
17 декабря Apache выпустил Log4j 2.17.0. Это третий релиз за две недели, и он наконец закрывает всю цепочку: 2.15.0 не полностью фиксил CVE-2021-44228, 2.16.0 ввёл CVE-2021-45105 через рекурсивный lookup. Теперь версия для обновления одна - 2.17.0. Всё, что ниже, включая 2.16.0, - не финальное решение.
Мы уже разбирали три волны патчинга и как координировали восемь клиентов параллельно. Сейчас хочется поговорить о другом: о том, что Log4Shell показал про инвентаризацию зависимостей. Разница между «есть SBOM» и «нет SBOM» оказалась не академической.
Три дня против двух часов
Среди клиентов, с которыми мы работали в первые дни после публикации PoC, была принципиальная разница в стартовой ситуации.
Те, у кого был актуальный инвентарь компонентов - пусть даже неполный - понимали периметр поиска за первые час-два. Мы знали, где стоит Log4j, откуда он пришёл - напрямую или транзитивно, - и могли приоритизировать: что торчит наружу, что внутри, что в CI/CD.
Те, у кого инвентаря не было, потратили три дня только на то, чтобы понять, где вообще искать. Три дня из примерно десяти, пока ситуация оставалась горячей. Это grep по репозиториям, ручной обход серверов, звонки командам разработки с вопросом «вы случайно не используете Log4j?». И это при том, что к тому моменту в каждом публичном сканере уже были активные попытки эксплуатации.
Разница не в том, что одни умнее других. Разница в том, что один вопрос - «где у нас Log4j» - решался за два часа или за три дня в зависимости от наличия инвентаря.
Что конкретно помогает
Мы использовали связку Syft + Grype - о которой писали в сентябре в контексте container images. В ситуации с Log4Shell эта же цепочка отработала по-боевому.
Syft генерирует SBOM - список всех компонентов с версиями - по образу или файловой системе. Grype берёт этот SBOM и проверяет по базам CVE. Для нашей задачи это означало: берёшь все production-образы, прогоняешь через Syft, фильтруешь результат по log4j, смотришь версии. На среду из нескольких десятков образов - около двух часов с учётом разбора результатов и составления списка приоритетов.
Команда, которую мы запускали для первичного анализа, выглядела примерно так:
# Генерация SBOM для образа
syft myapp:prod -o cyclonedx-json > sbom.json
# Проверка на известные CVE, включая Log4Shell
grype sbom:sbom.json --fail-on critical
Важный момент: Syft ловит Log4j в jar-файлах даже если он упакован внутри fat jar - то есть транзитивные зависимости тоже видны. Это именно тот случай, когда «мы не используем Log4j напрямую» не означает «мы не уязвимы».
Что осложняло картину
Несколько наблюдений из процесса, которые имеет смысл зафиксировать.
Вендорские компоненты - отдельная история. Syft анализирует то, что есть в образе. Но если компонент поставляется в виде закрытого бинарника или дистрибутива без исходников - инструмент может его не увидеть или увидеть не полностью. По нескольким позициям у клиентов пришлось идти к документации вендора или напрямую спрашивать у производителя оборудования. Ответы приходили не быстро.
Старые артефакты. Обнаружились образы, которые никто не деплоил месяцами, но они всё ещё висели в registry и теоретически могли быть запущены. Их тоже надо сканировать, но кто несёт ответственность за обновление - вопрос организационный, не технический.
Компоненты вне Docker. Часть инфраструктуры работает не в контейнерах. Jenkins-агенты, legacy Java-сервисы на bare-metal или VM, административные панели. Для них Syft тоже работает - через syft packages dir:/path или syft packages java-archive:/path/to.jar, - но автоматической инвентаризации этого слоя у клиентов не было. Пришлось делать вручную.
Почему SBOM - это не разовая работа
Log4Shell - хороший пример того, почему инвентарь компонентов должен быть живым, а не снимком раз в год.
Мы генерировали SBOM для контейнеров в сентябре. К декабрю часть образов успела обновиться, часть нет. Без актуального инвентаря сентябрьский список дал бы ложное чувство уверенности - «мы смотрели, всё в порядке». С актуальным - была понятна разница между тем, что было, и тем, что стало.
Разумный минимум, к которому мы пришли по итогам этих двух недель:
- SBOM генерируется в CI. При каждой сборке образа - не вручную и не раз в квартал. Syft встраивается в пайплайн как шаг после сборки, артефакт сохраняется вместе с образом.
- Grype запускается там же. Критические CVE блокируют деплой. Остальное - в тикет с дедлайном.
- Вне Docker тоже нужен инвентарь. Пусть это будет таблица с компонентами, версиями и ответственными - но она должна существовать и обновляться при каждом изменении.
Где мы сейчас
Log4j 2.17.0 - финальная точка в части патчинга самой библиотеки. Вендорские компоненты всё ещё ждут обновлений от производителей, и по ним mitigation остаётся активным. Это не закончится в этот понедельник.
Организационный вывод, который мы сделали: аудит компонентного состава - это не то, что делается перед инцидентом «для галочки». Это инструмент, который определяет, сколько времени занимает ответ на вопрос «где у нас X» в момент, когда этот вопрос стал срочным. Log4Shell показал разницу между двумя часами и тремя днями. Достаточно показательно.
- SBOM на практике: Syft + Grype для container images и интеграция в CI · 13 сентября 2021