SBOM для .NET в Docker: перестаньте пытаться заставить один инструмент видеть всё
Как отслеживать зависимости NuGet и пакеты ОС контейнера для Docker-образа .NET с помощью CycloneDX, Syft и Dependency-Track -- и почему одного SBOM недостаточно.
В одном из тредов по DevOps задали вопрос, который встречается постоянно: “Как одновременно отслеживать зависимости NuGet и пакеты ОС контейнера для приложения .NET, поставляемого как Docker-образ?”. Автор уже был близок к правильному подходу: CycloneDX для графа .NET-проекта, Syft для образа, затем загрузка в Dependency-Track.
Источник: тред на Reddit.
Один SBOM часто оказывается неверной целью
Образ контейнера содержит как минимум две вселенные зависимостей:
- Зависимости приложения: пакеты NuGet, разрешённые на этапе сборки (ваш мир
*.deps.json). - Зависимости образа: пакеты ОС и слои базового образа (ваш мир
apt,apk, libc, OpenSSL).
В .NET 9 и .NET 10 любая из этих сторон может случайно исчезнуть из поля зрения:
- Сканеры образов могут не увидеть версии NuGet, потому что не читают граф проекта.
- Инструменты SBOM уровня приложения не увидят пакеты ОС базового образа, потому что не сканируют слои.
Именно поэтому “пусть один инструмент делает всё” обычно заканчивается слепыми зонами.
Создавайте два SBOM и сохраняйте происхождение
Вот практический конвейер:
- SBOM A (уровень приложения): создаётся из решения или проекта на этапе сборки.
- Инструмент: cyclonedx-dotnet
- SBOM B (уровень образа): создаётся из готового образа.
- Инструмент: Syft
- Загрузка и мониторинг: оба загружаются в Dependency-Track.
Главное — происхождение. Вы хотите уметь отвечать на вопрос: “Эта CVE в моём базовом образе или в моём графе NuGet?” — без догадок.
Минимальные команды, которые можно вставить в CI
# App SBOM (NuGet focused)
dotnet tool install --global CycloneDX
dotnet CycloneDX .\MyApp.sln -o .\sbom --json
# Image SBOM (OS packages and what the image reveals)
docker build -t myapp:ci .
syft myapp:ci -o cyclonedx-json=.\sbom\container.cdx.json
Если хотите, чтобы SBOM приложения соответствовал тому, что действительно поставляется, создавайте его из того же коммита, из которого собран образ контейнера, и храните оба артефакта вместе.
Стоит ли объединять BOM?
Если ваш главный вопрос — “стоит ли объединить эти BOM в один?”, мой ответ по умолчанию: по умолчанию не объединять.
- Держите их отдельно, чтобы оповещения оставались действенными.
- Если нужен единый отчёт о соответствии, объединяйте на уровне отчёта, а не сплющивая происхождение в самом SBOM.
В Dependency-Track это часто превращается в два проекта: myapp и myapp-image. Это не лишняя сложность. Это более чистая модель.
Почему Syft “пропускает NuGet” и что с этим делать
Syft силён в работе с образами и файловыми системами. Он сообщает то, что может опознать из того, что видит. Если нужны достоверные зависимости NuGet, генерируйте их из графа проекта средствами CycloneDX.
Можете попробовать сканировать опубликованный вывод (например syft dir:publish/), но относитесь к этому как к дополнению. Вопрос “какие пакеты и каких версий мы используем?” принадлежит графу сборки, а не сканированию слоёв.
Если вы строите сервисы на .NET 10 в контейнерах, два SBOM — честный ответ. Вы получаете лучшее покрытие, более чёткую ответственность и меньше ложных срабатываний, съедающих спринт.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.