SBOM в GitFlic CI: генерация, подпись и хранение артефактов под требования ФСТЭК
ФСТЭК рекомендует SBOM как меру защиты для разработчиков ПО для КИИ. Настраиваем генерацию и подпись в GitFlic CI, разбираем форматы и интеграцию с CMDB.
ФСТЭК рекомендует внедрение SBOM как обязательную меру защиты для разработчиков ПО для КИИ
ФСТЭК на прошлой неделе выпустил методические рекомендации, в которых SBOM (Software Bill of Materials) фигурирует уже не как опциональная практика, а как обязательная защитная мера для организаций, разрабатывающих ПО для объектов КИИ. До этого момента нас регулярно спрашивали клиенты: «А зачем нам SBOM, если мы всё равно не публикуем open source?» Теперь ответ на этот вопрос дал сам регулятор, что значительно упрощает внутренние переговоры.
Что именно ФСТЭК имеет в виду
В рекомендациях фигурируют два требования, которые касаются нас напрямую. Первое - наличие актуального перечня компонентов для каждой версии ПО, поставляемого на объект КИИ. Второе - возможность проверить целостность этого перечня, то есть убедиться, что он не был модифицирован после формирования.
С точки зрения форматов регулятор называет CycloneDX и SPDX как приемлемые - оба в формате JSON или XML. SPDX исторически чаще встречается в «бумажных» требованиях американских вендоров, CycloneDX проще интегрируется с инструментарием анализа уязвимостей. Мы в итоге остановились на CycloneDX JSON как основном формате: он компактнее, хорошо читается Grype и Dependency-Track.
GitFlic CI: откуда стартовали
Большинство наших клиентов на КИИ к этому моменту уже мигрировали с GitHub/GitLab на GitFlic - это стало нормой после соответствующих требований регулятора в 2023-2024 годах. GitFlic CI по функциональности близок к GitLab CI с поправкой на то, что часть плагинной экосистемы там своя, часть вообще отсутствует.
Хорошая новость: Syft и Cosign, которые мы используем для генерации и подписи SBOM, - это просто бинарники. Их можно положить в образ раннера или подтянуть в pipeline как артефакты. GitFlic это не ограничивает.
Что мы настроили в pipeline
Шаг SBOM-генерации выглядит примерно так:
sbom-generate:
stage: security
image: registry.internal/devtools/syft:latest
script:
- syft packages dir:. --output cyclonedx-json=sbom.cdx.json
- syft packages dir:. --output spdx-json=sbom.spdx.json
artifacts:
paths:
- sbom.cdx.json
- sbom.spdx.json
expire_in: 1 year
Два формата параллельно - для того чтобы при необходимости предоставить тот, который просит конкретный заказчик или проверяющий. Хранение артефактов год - не потому что нам так нравится, а потому что это минимальный разумный горизонт при проверках КИИ.
Подпись. Здесь пришлось немного повозиться. Cosign поддерживает несколько режимов подписи; в изолированном контуре без доступа к Sigstore мы используем режим с ключевой парой:
sbom-sign:
stage: security
image: registry.internal/devtools/cosign:latest
needs: [sbom-generate]
script:
- cosign sign-blob --key env://COSIGN_PRIVATE_KEY
--output-signature sbom.cdx.json.sig
sbom.cdx.json
artifacts:
paths:
- sbom.cdx.json.sig
expire_in: 1 year
COSIGN_PRIVATE_KEY - переменная из хранилища секретов GitFlic, ключ генерируется один раз и хранится там. Публичный ключ - в репозитории рядом с политикой подписи. Проверка при получении: cosign verify-blob --key cosign.pub --signature sbom.cdx.json.sig sbom.cdx.json.
Это не keyless Sigstore, но для изолированного контура это единственный рабочий вариант. ФСТЭК в рекомендациях не требует конкретного механизма подписи, лишь «возможность проверки целостности» - мы эту галку закрываем.
Хранение и CMDB
Артефакты в GitFlic CI живут в хранилище самого GitFlic - это удобно для разработчиков, но не очень удобно для инвентаризации. CMDB не умеет ходить в GitFlic API за SBOM-артефактами сама по себе.
Мы решили это простым шагом в pipeline - после генерации и подписи SBOM отправляется в Nexus Repository, где лежит в отдельном raw-репозитории по схеме {project}/{version}/sbom.cdx.json. Nexus умеют опрашивать и Dependency-Track, и несколько CMDB-систем через REST.
Dependency-Track в нашей конфигурации играет роль промежуточного слоя: принимает SBOM, анализирует состав на предмет CVE (используя локально загруженные базы NVD и OSV), ведёт историю версий. Из Dependency-Track уже по API данные о компонентах уходят в CMDB - как список артефактов, лицензий и известных уязвимостей на момент сборки.
Это не real-time инвентаризация, но для целей аудита - вполне: проверяющий может получить состав любой задеплоенной версии и убедиться, что SBOM не редактировался после подписи.
Что не заработало с первого раза
Syft генерирует SBOM из того, что видит в файловой системе: package.json, go.sum, requirements.txt, pom.xml. Если зависимости подтягиваются в момент сборки внутри docker build, а pipeline работает с исходниками - Syft их не видит. Для Java-проектов с fat JAR нам пришлось добавить отдельный шаг: сначала собрать артефакт, потом прогнать Syft по нему как по директории.
Второй момент - транзитивные зависимости. CycloneDX-формат умеет описывать дерево зависимостей, Syft это поддерживает, но по умолчанию выдаёт плоский список. Для полного дерева нужен флаг --scope all-layers при работе с образами или настройка через syft.yaml. Без этого SBOM технически валидный, но неполный.
Где сейчас стоит точка
Рекомендации ФСТЭК - это рекомендации, а не требования НПА. Пока что. Но по нашему опыту, расстояние между «рекомендует» и «проверяет» у регулятора не очень большое, и готовить почву лучше сейчас, чем объяснять на проверке, почему ничего не сделано.
Весь описанный стек - Syft, Cosign, Dependency-Track, Nexus - работает в изолированном контуре без внешнего интернета, что для КИИ принципиально. Интеграция с конкретной CMDB требует индивидуальной настройки, но базовый pipeline занимает день-два в рамках аудита и настройки DevSecOps-процессов.
Тема SBOM-дифференцирования между версиями (что изменилось в составе с прошлого релиза) - отдельная история, которую мы пока только пробуем автоматизировать.