ADG Оставить заявку
Блог Информационная безопасность 5 мин чтения

DevSecOps для КИИ: Trivy + Cosign в Gitflic CI и что делать с legacy-образами без тегов

Для КИИ-клиентов настраиваем обязательный scan каждого образа в Harbor через Trivy и Cosign в Gitflic CI. Рассказываем про интеграцию и про проблему legacy без тегов.

Контекст момента

DevSecOps зреет в корпоративном секторе РФ: обязательная проверка артефактов в CI/CD для КИИ

Год назад разговор с КИИ-клиентами про сканирование образов звучал примерно так: «мы понимаем, что надо, но не горит». Сейчас не так. После обновления методрекомендаций ФСТЭК и нескольких проверок, о которых разошлись слухи в профессиональном сообществе, вопрос «когда настроим» превратился в «почему ещё не настроили». Мы сейчас идём по нескольким КИИ-объектам из клиентского портфеля и везде поднимаем одну и ту же схему: Harbor как реестр, Trivy как сканер, Cosign для подписи, и всё это завязано в Gitflic CI.

Почему Harbor без политик - не защита

Harbor стоит у большинства наших клиентов уже несколько лет - его выбирали ещё когда GitLab Container Registry казался избыточным. Но стоит - не значит работает как защита. Типичная картина при аудите: образы пушатся, лежат, никто их не сканирует, политик блокировки нет. Это просто файловый сервер с красивым интерфейсом.

Harbor 2.x умеет встраивать Trivy как внутренний сканер - включается в настройках за несколько кликов. После включения каждый новый образ при пуше автоматически уходит на сканирование. Дальше настраивается политика: образы с критическими уязвимостями без доступного фикса блокируются при pull. Важный нюанс, который сначала вызывает вопросы у команд: образ попадает в реестр, но не становится доступным для pull, пока сканирование не завершилось. Первый раз это выглядит как баг. Надо объяснять заранее.

Для КИИ-контекста отдельно интересен режим сканирования на наличие вредоносных компонентов - Trivy проверяет базы Trivy Advisory DB и GHSA. Это не закрывает zero-day, но known-malicious компоненты из последних supply chain инцидентов ловит нормально. Мы проверяли на реальных кейсах из нашего мартовского разбора.

Gitflic CI: как интегрировали Cosign

С подписью образов через Cosign в Gitflic CI есть одна особенность, которую нужно учитывать: Gitflic не поддерживает OIDC keyless signing так же нативно, как GitLab через id_tokens (OIDC). Пришлось идти через классический вариант - пара ключей, ключ в CI-переменной.

Схема stage в пайплайне:

scan-image:
  stage: security
  image: registry.internal/devtools/trivy:0.52
  needs: [build]
  script:
    - trivy image
        --severity CRITICAL,HIGH
        --exit-code 1
        --ignore-unfixed
        ${HARBOR_HOST}/${CI_PROJECT_PATH}:${CI_COMMIT_SHA}
  only:
    - main
    - /^release\/.*/

sign-image:
  stage: security
  image: registry.internal/devtools/cosign:2.4
  needs: [scan-image]
  script:
    - echo "${COSIGN_PRIVATE_KEY}" | cosign sign --key /dev/stdin
        ${HARBOR_HOST}/${CI_PROJECT_PATH}:${CI_COMMIT_SHA}
  only:
    - main
    - /^release\/.*/

needs: [scan-image] здесь принципиален: подпись ставится только после успешного прохождения scan. Если Trivy вернул exit code 1 - sign-image не запустится, образ не будет подписан, и дальнейшие попытки задеплоить его в Kubernetes упрутся в Kyverno-политику.

Cosign хранит подписи в Harbor как OCI-артефакты рядом с образом - никакого отдельного хранилища не нужно. Harbor 2.x поддерживает это нативно.

Kyverno-политика закрывает последний рубеж: в namespace production и staging запрещены Pod-ы с образами без валидной Cosign-подписи от нашего internal CA. Dev-namespace намеренно оставляем без принудительной верификации - разработчики собирают образы локально, блокировать dev болезненно и контрпродуктивно.

Проблема legacy-образов без тегов

Это то, что неожиданно съело больше всего времени. У каждого клиента в Harbor накопился слой образов, которые пушились годами без нормальной политики тегирования: latest, безымянные слои от промежуточных сборок, артефакты полузабытых проектов. Trivy их не сканировал никогда.

Проблема в том, что часть этих образов всё ещё используется. Не активно, но используется - в batch-задачах, в legacy-скриптах деплоя, в cron-контейнерах, которые «всегда так работали». Тупо заблокировать их нельзя: завалишь какой-нибудь ночной процесс и узнаешь об этом утром от заказчика.

Мы выработали такой подход:

  • Инвентаризация через Harbor API - скрипт обходит все репозитории, собирает образы старше 180 дней без тегов кроме latest, смотрит дату последнего pull. Образы, которые не тянули больше года, выносим в отдельный список «кандидаты на архив».
  • Форсированное сканирование - для образов с активными pulls (даже раз в квартал) запускаем Trivy принудительно через Harbor API: POST /api/v2.0/repositories/{repo}/artifacts/{digest}/scan. Это не блокирует образ, но даёт нам отчёт по уязвимостям.
  • Постепенная блокировка - образы с критическими CVE, которые реально тянутся в продакшн, выносим на отдельные переговоры с командой клиента: либо обновляете базовый образ, либо мы фиксируем это как accepted risk с обоснованием для аудитора ФСТЭК.

Принципиально важный момент: принятый риск для КИИ-объектов должен быть задокументирован. «Мы знали, но не обновили» без бумаги - это замечание на проверке. «Мы знали, оценили, приняли, вот обоснование и дата пересмотра» - это управляемая история.

Что получается в итоге

Полный пайплайн для нового образа выглядит так: сборка - Trivy-скан (блокирующий для main/release) - Cosign-подпись - пуш в Harbor - Kyverno в Kubernetes. Образ, который не прошёл хотя бы одну ступень, до production не доберётся.

Для legacy-образов пока компромисс: сканируем принудительно, документируем риски, постепенно вытесняем самые проблемные. Полный закрытый контур - это процесс на несколько месяцев, а не спринт.

Если ваш объект КИИ готовится к аудиту или хочет заранее разобраться с состоянием реестра образов - это хорошо ложится в рамки аудита безопасности, где мы разбираем текущее состояние и выстраиваем конкретный план.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.